From ca41b8eceedaf71da198b01e666c4864f8b3e472 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Tue, 11 Jun 2024 15:03:04 +0800 Subject: [PATCH 1/9] add JsonDateTimeErrorHolding --- .../Serialization/DateTimeJsonConverter.cs | 9 +++++++-- .../Data/ContentItemTests.cs | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs b/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs index 9c2915aa2dc..5da37998b48 100644 --- a/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs +++ b/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs @@ -14,9 +14,14 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso throw new ArgumentException("Unexpected type to convert.", nameof(typeToConvert)); } - if (!reader.TryGetDateTime(out DateTime value) && DateTime.TryParse(reader.GetString()!, out value)) + if (!reader.TryGetDateTime(out DateTime value)) { - return value; + var stringValue = reader.GetString(); + if (DateTime.TryParse(stringValue, out value)) + { + return value; + } + throw new JsonException($"Unable to convert \"{stringValue}\" to DateTime."); } return value; diff --git a/test/OrchardCore.Tests/Data/ContentItemTests.cs b/test/OrchardCore.Tests/Data/ContentItemTests.cs index 91494ff1605..e1ea266b78a 100644 --- a/test/OrchardCore.Tests/Data/ContentItemTests.cs +++ b/test/OrchardCore.Tests/Data/ContentItemTests.cs @@ -25,6 +25,15 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() "DateTimeFieldTest": { "Value": "2024-5-31 13:05" }, + "NullDateTimeFieldTest": { + "Value": null + }, + "EmptyDateTimeFieldTest": { + "Value": "" + }, + "ErrorFormatDateTimeFieldTest": { + "Value": "ErrorFormatValue" + }, "TimezoneDateTimeFieldTest": { "Value": "2022-12-13T21:02:18.399-05:00" }, @@ -40,8 +49,17 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() var dateField = jobject.SelectNode("DateFieldTest").ToObject(); var dateTimeField = jobject.SelectNode("DateTimeFieldTest").ToObject(); var timezoneDateTimeFieldTest = jobject.SelectNode("TimezoneDateTimeFieldTest").ToObject(); + var nullDateTimeField = jobject.SelectNode("NullDateTimeFieldTest").ToObject(); // Assert + + Assert.Null(nullDateTimeField.Value); + + var emptyValueTestexcepion = Assert.Throws(() => jobject.SelectNode("EmptyDateTimeFieldTest").ToObject()); + Assert.Equal("Unable to convert \"\" to DateTime.", emptyValueTestexcepion.Message); + + var errorFormatValueTestexcepion = Assert.Throws(() => jobject.SelectNode("ErrorFormatDateTimeFieldTest").ToObject()); + Assert.Equal("Unable to convert \"ErrorFormatValue\" to DateTime.", errorFormatValueTestexcepion.Message); Assert.Equal("13:05:00", timeField.Value.Value.ToString()); Assert.Equal("2024-05-31", dateField.Value.Value.ToString("yyyy-MM-dd")); Assert.Equal("2024-05-31 13:05", dateTimeField.Value.Value.ToString("yyyy-MM-dd HH:mm")); @@ -49,6 +67,8 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() Assert.Equal("2024-05-31T00:00:00Z", JObject.FromObject(dateField).SelectNode("Value").ToString()); Assert.Equal("2024-05-31T13:05:00Z", JObject.FromObject(dateTimeField).SelectNode("Value").ToString()); + Assert.Null(JObject.FromObject(nullDateTimeField).SelectNode("Value")); + var utcTime = TimeZoneInfo.ConvertTimeToUtc(timezoneDateTimeFieldTest.Value.Value); Assert.Equal("2022-12-14 02:02:18", utcTime.ToString("yyyy-MM-dd HH:mm:ss")); } From dca01beedec695a0093fbcc57c245ffd3d884986 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Tue, 11 Jun 2024 15:51:09 +0800 Subject: [PATCH 2/9] update argument names --- test/OrchardCore.Tests/Data/ContentItemTests.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/OrchardCore.Tests/Data/ContentItemTests.cs b/test/OrchardCore.Tests/Data/ContentItemTests.cs index e1ea266b78a..7111496cd17 100644 --- a/test/OrchardCore.Tests/Data/ContentItemTests.cs +++ b/test/OrchardCore.Tests/Data/ContentItemTests.cs @@ -25,10 +25,10 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() "DateTimeFieldTest": { "Value": "2024-5-31 13:05" }, - "NullDateTimeFieldTest": { + "NullValueDateTimeFieldTest": { "Value": null }, - "EmptyDateTimeFieldTest": { + "EmptyValueDateTimeFieldTest": { "Value": "" }, "ErrorFormatDateTimeFieldTest": { @@ -49,17 +49,18 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() var dateField = jobject.SelectNode("DateFieldTest").ToObject(); var dateTimeField = jobject.SelectNode("DateTimeFieldTest").ToObject(); var timezoneDateTimeFieldTest = jobject.SelectNode("TimezoneDateTimeFieldTest").ToObject(); - var nullDateTimeField = jobject.SelectNode("NullDateTimeFieldTest").ToObject(); + var nullValueDateTimeField = jobject.SelectNode("NullValueDateTimeFieldTest").ToObject(); // Assert - Assert.Null(nullDateTimeField.Value); + Assert.Null(nullValueDateTimeField.Value); - var emptyValueTestexcepion = Assert.Throws(() => jobject.SelectNode("EmptyDateTimeFieldTest").ToObject()); + var emptyValueTestexcepion = Assert.Throws(() => jobject.SelectNode("EmptyValueDateTimeFieldTest").ToObject()); Assert.Equal("Unable to convert \"\" to DateTime.", emptyValueTestexcepion.Message); var errorFormatValueTestexcepion = Assert.Throws(() => jobject.SelectNode("ErrorFormatDateTimeFieldTest").ToObject()); Assert.Equal("Unable to convert \"ErrorFormatValue\" to DateTime.", errorFormatValueTestexcepion.Message); + Assert.Equal("13:05:00", timeField.Value.Value.ToString()); Assert.Equal("2024-05-31", dateField.Value.Value.ToString("yyyy-MM-dd")); Assert.Equal("2024-05-31 13:05", dateTimeField.Value.Value.ToString("yyyy-MM-dd HH:mm")); @@ -67,7 +68,7 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() Assert.Equal("2024-05-31T00:00:00Z", JObject.FromObject(dateField).SelectNode("Value").ToString()); Assert.Equal("2024-05-31T13:05:00Z", JObject.FromObject(dateTimeField).SelectNode("Value").ToString()); - Assert.Null(JObject.FromObject(nullDateTimeField).SelectNode("Value")); + Assert.Null(JObject.FromObject(nullValueDateTimeField).SelectNode("Value")); var utcTime = TimeZoneInfo.ConvertTimeToUtc(timezoneDateTimeFieldTest.Value.Value); Assert.Equal("2022-12-14 02:02:18", utcTime.ToString("yyyy-MM-dd HH:mm:ss")); From f64b1fba86ccc068d0e927b24145ff09412c3192 Mon Sep 17 00:00:00 2001 From: Tony Han Date: Tue, 11 Jun 2024 18:42:32 +0800 Subject: [PATCH 3/9] update --- .../Data/ContentItemTests.cs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/test/OrchardCore.Tests/Data/ContentItemTests.cs b/test/OrchardCore.Tests/Data/ContentItemTests.cs index 7111496cd17..3ebfdea3080 100644 --- a/test/OrchardCore.Tests/Data/ContentItemTests.cs +++ b/test/OrchardCore.Tests/Data/ContentItemTests.cs @@ -9,6 +9,24 @@ namespace OrchardCore.Tests.Data { public class ContentItemTests { + [Fact] + public void NullValueDateTimeFieldSerialisationTest() + { + // Arrange + var jsonStr = """ + { + "NullValueDateTimeFieldTest": { + "Value": null + } + } + """; + + var jobject = JsonNode.Parse(jsonStr); + + var nullValueDateTimeField = jobject.SelectNode("NullValueDateTimeFieldTest").ToObject(); + Assert.Null(nullValueDateTimeField.Value); + Assert.Null(JObject.FromObject(nullValueDateTimeField).SelectNode("Value")); + } /// /// To validate /// and @@ -25,9 +43,6 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() "DateTimeFieldTest": { "Value": "2024-5-31 13:05" }, - "NullValueDateTimeFieldTest": { - "Value": null - }, "EmptyValueDateTimeFieldTest": { "Value": "" }, @@ -49,12 +64,9 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() var dateField = jobject.SelectNode("DateFieldTest").ToObject(); var dateTimeField = jobject.SelectNode("DateTimeFieldTest").ToObject(); var timezoneDateTimeFieldTest = jobject.SelectNode("TimezoneDateTimeFieldTest").ToObject(); - var nullValueDateTimeField = jobject.SelectNode("NullValueDateTimeFieldTest").ToObject(); // Assert - Assert.Null(nullValueDateTimeField.Value); - var emptyValueTestexcepion = Assert.Throws(() => jobject.SelectNode("EmptyValueDateTimeFieldTest").ToObject()); Assert.Equal("Unable to convert \"\" to DateTime.", emptyValueTestexcepion.Message); @@ -68,7 +80,6 @@ public void JsonNode_WhenParseCalled_ConvertShortTimeFormatToTimeField() Assert.Equal("2024-05-31T00:00:00Z", JObject.FromObject(dateField).SelectNode("Value").ToString()); Assert.Equal("2024-05-31T13:05:00Z", JObject.FromObject(dateTimeField).SelectNode("Value").ToString()); - Assert.Null(JObject.FromObject(nullValueDateTimeField).SelectNode("Value")); var utcTime = TimeZoneInfo.ConvertTimeToUtc(timezoneDateTimeFieldTest.Value.Value); Assert.Equal("2022-12-14 02:02:18", utcTime.ToString("yyyy-MM-dd HH:mm:ss")); From e51e5247d3984f35b8ac0fc417a3d5d568bb9abc Mon Sep 17 00:00:00 2001 From: Tony Han Date: Thu, 25 Jul 2024 00:03:31 +0800 Subject: [PATCH 4/9] Update src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs Co-authored-by: Hisham Bin Ateya --- .../Json/Serialization/DateTimeJsonConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs b/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs index 5da37998b48..faf55f7f096 100644 --- a/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs +++ b/src/OrchardCore/OrchardCore.Abstractions/Json/Serialization/DateTimeJsonConverter.cs @@ -21,6 +21,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso { return value; } + throw new JsonException($"Unable to convert \"{stringValue}\" to DateTime."); } From 84ff14b661e819d466bb23a5e0846de35e8ba946 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Wed, 14 Aug 2024 12:18:15 -0700 Subject: [PATCH 5/9] Format code --- .editorconfig | 6 +- .gitattributes | 4 - Directory.Packages.props | 1 + .../OrchardCore.Commons.props | 5 +- .../AdminAreaControllerRouteMapper.cs | 77 +- .../OrchardCore.Admin/AdminFilter.cs | 85 +- .../OrchardCore.Admin/AdminMenu.cs | 57 +- .../OrchardCore.Admin/AdminMenuFilter.cs | 105 +- .../AdminPageRouteModelConvention.cs | 41 +- .../AdminPageRouteModelProvider.cs | 129 +- .../OrchardCore.Admin/AdminThemeSelector.cs | 63 +- .../OrchardCore.Admin/AdminThemeService.cs | 59 +- .../OrchardCore.Admin/AdminZoneFilter.cs | 37 +- .../Controllers/AdminController.cs | 13 +- .../Drivers/AdminSiteSettingsDisplayDriver.cs | 87 +- .../OrchardCore.Admin/Startup.cs | 147 +- .../ViewModels/AdminSettingsViewModel.cs | 15 +- .../Controllers/DashboardController.cs | 283 ++- .../DashboardFeature.cs | 9 +- .../Drivers/DashboardContentDisplayDriver.cs | 147 +- .../Drivers/DashboardPartDisplayDriver.cs | 51 +- .../Indexes/DashboardPartIndex.cs | 51 +- .../OrchardCore.AdminDashboard/Migrations.cs | 87 +- .../Models/DashboardPart.cs | 13 +- .../Services/AdminDashboardService.cs | 33 +- .../Services/IAdminDashboardService.cs | 15 +- .../OrchardCore.AdminDashboard/Startup.cs | 61 +- .../ViewModels/AdminDashboardViewModel.cs | 19 +- .../ViewModels/DashboardPartViewModel.cs | 23 +- .../ViewModels/DashboardWrapper.cs | 14 +- .../OrchardCore.AdminMenu/AdminMenu.cs | 55 +- .../AdminNodes/LinkAdminNode.cs | 25 +- .../AdminNodes/LinkAdminNodeDriver.cs | 119 +- .../LinkAdminNodeNavigationBuilder.cs | 127 +- .../AdminNodes/LinkAdminNodeViewModel.cs | 29 +- .../AdminNodes/PermissionViewModel.cs | 11 +- .../AdminNodes/PlaceholderAdminNode.cs | 21 +- .../AdminNodes/PlaceholderAdminNodeDriver.cs | 103 +- .../PlaceholderAdminNodeNavigationBuilder.cs | 81 +- .../PlaceholderAdminNodeViewModel.cs | 23 +- .../Controllers/MenuController.cs | 431 ++-- .../Controllers/NodeController.cs | 509 +++-- .../Deployment/AdminMenuDeploymentSource.cs | 57 +- .../Deployment/AdminMenuDeploymentStep.cs | 17 +- .../AdminMenuDeploymentStepDriver.cs | 27 +- .../Models/AdminMenuList.cs | 15 +- .../Recipes/AdminMenuStep.cs | 71 +- ...AdminMenuNavigationProvidersCoordinator.cs | 99 +- .../Services/AdminMenuService.cs | 79 +- .../Services/IAdminMenuService.cs | 57 +- .../OrchardCore.AdminMenu/Startup.cs | 31 +- .../ViewModels/AdminMenuCreateViewModel.cs | 11 +- .../ViewModels/AdminMenuEditViewModel.cs | 13 +- .../ViewModels/AdminMenuListViewModel.cs | 63 +- .../ViewModels/AdminNodeEditViewModel.cs | 23 +- .../ViewModels/AdminNodeListViewModel.cs | 11 +- .../Drivers/AliasPartDisplayDriver.cs | 63 +- .../GraphQL/AliasInputObjectType.cs | 15 +- .../GraphQL/AliasPartIndexAliasProvider.cs | 29 +- .../GraphQL/AliasQueryObjectType.cs | 15 +- .../OrchardCore.Alias/GraphQL/Startup.cs | 19 +- .../Handlers/AliasPartHandler.cs | 209 +- .../Indexes/AliasPartIndex.cs | 113 +- .../Indexing/AliasPartIndexHandler.cs | 21 +- .../OrchardCore.Alias/Migrations.cs | 143 +- .../OrchardCore.Alias/Models/AliasPart.cs | 19 +- .../Models/AliasPartExtensions.cs | 33 +- .../AliasPartContentHandleProvider.cs | 45 +- .../Settings/AliasPartSettings.cs | 31 +- .../AliasPartSettingsDisplayDriver.cs | 79 +- .../Settings/AliasPartSettingsViewModel.cs | 15 +- .../OrchardCore.Alias/Startup.cs | 65 +- .../ViewModels/AliasPartViewModel.cs | 21 +- .../OrchardCore.Apis.GraphQL/AdminMenu.cs | 43 +- .../Controllers/AdminController.cs | 15 +- .../Extensions/DocumentWriterExtensions.cs | 41 +- .../GraphQLMiddleware.cs | 275 ++- .../GraphQLNamedQueryRequest.cs | 17 +- .../OrchardFieldNameConverter.cs | 39 +- .../Services/SchemaService.cs | 145 +- .../OrchardCore.Apis.GraphQL/Startup.cs | 115 +- .../MaxNumberOfResultsValidationRule.cs | 97 +- .../RequiresPermissionValidationRule.cs | 173 +- .../Controllers/AdminController.cs | 243 ++- .../Drivers/AuditTrailEventDisplayDriver.cs | 53 +- .../Drivers/AuditTrailOptionsDisplayDriver.cs | 107 +- .../AuditTrailSettingsDisplayDriver.cs | 167 +- ...AuditTrailTrimmingSettingsDisplayDriver.cs | 75 +- .../Indexes/AuditTrailEventIndexProvider.cs | 37 +- .../OrchardCore.AuditTrail/Migrations.cs | 55 +- .../Navigation/AuditTrailAdminMenu.cs | 53 +- .../Navigation/AuditTrailSettingsAdminMenu.cs | 59 +- .../Services/AuditTrailBackgroundTask.cs | 79 +- .../Services/AuditTrailIdGenerator.cs | 19 +- .../Services/AuditTrailManager.cs | 273 ++- .../Services/CorrelationIdFilterNode.cs | 25 +- .../Services/DateTimeParser.cs | 381 ++-- .../DefaultAuditTrailAdminListFilterParser.cs | 21 +- ...efaultAuditTrailAdminListFilterProvider.cs | 357 ++-- .../DefaultAuditTrailAdminListQueryService.cs | 191 +- .../Settings/AuditTrailCategorySettings.cs | 11 +- .../Settings/AuditTrailEventSettings.cs | 13 +- .../Settings/AuditTrailSettings.cs | 11 +- .../Settings/AuditTrailTrimmingSettings.cs | 13 +- .../OrchardCore.AuditTrail/Startup.cs | 153 +- .../AuditTrailCategorySettingsViewModel.cs | 15 +- .../AuditTrailEventSettingsViewModel.cs | 23 +- .../ViewModels/AuditTrailEventViewModel.cs | 11 +- .../ViewModels/AuditTrailItemViewModel.cs | 11 +- .../ViewModels/AuditTrailListViewModel.cs | 15 +- .../ViewModels/AuditTrailSettingsViewModel.cs | 11 +- .../AuditTrailTrimmingSettingsViewModel.cs | 13 +- .../AutoSetupMiddleware.cs | 429 ++-- .../Extensions/DistributedLockExtensions.cs | 73 +- .../Options/AutoSetupOptions.cs | 93 +- .../Options/LockOptions.cs | 25 +- .../Options/TenantSetupOptions.cs | 291 ++- .../OrchardCore.AutoSetup/Startup.cs | 165 +- .../Drivers/AutoroutePartDisplayDriver.cs | 181 +- .../GraphQL/AutorouteInputObjectType.cs | 15 +- .../AutoroutePartIndexAliasProvider.cs | 29 +- .../GraphQL/AutorouteQueryObjectType.cs | 15 +- .../OrchardCore.Autoroute/GraphQL/Startup.cs | 19 +- .../Handlers/AutorouteContentHandler.cs | 47 +- .../Handlers/AutoroutePartHandler.cs | 629 +++--- .../Handlers/DefaultRouteContentHandler.cs | 45 +- .../Indexing/AutoroutePartIndexHandler.cs | 25 +- .../OrchardCore.Autoroute/Migrations.cs | 109 +- .../Models/AutoroutePartExtensions.cs | 71 +- .../Models/AutoroutePartSettings.cs | 87 +- .../AutorouteMetaWeblogDriver.cs | 43 +- .../RemotePublishingStartup.cs | 13 +- .../Routing/AutorouteTransformer.cs | 53 +- .../Routing/AutorouteValuesAddressScheme.cs | 127 +- .../AutoroutePartSettingsDisplayDriver.cs | 115 +- .../Sitemaps/AutorouteContentTypeProvider.cs | 59 +- .../OrchardCore.Autoroute/Startup.cs | 117 +- .../AutoroutePartSettingsViewModel.cs | 25 +- .../ViewModels/AutoroutePartViewModel.cs | 33 +- .../OrchardCore.BackgroundTasks/AdminMenu.cs | 41 +- .../Controllers/BackgroundTaskController.cs | 409 ++-- .../Services/AlwaysHasChangedToken.cs | 49 +- .../Services/BackgroundTaskManager.cs | 75 +- .../BackgroundTaskSettingsProvider.cs | 37 +- .../OrchardCore.BackgroundTasks/Startup.cs | 19 +- .../BackgroundTaskIndexViewModel.cs | 31 +- .../ViewModels/BackgroundTaskViewModel.cs | 29 +- .../ContentPickerAdminController.cs | 147 +- ...lizationSetContentPickerAdminController.cs | 119 +- .../Controllers/UserPickerAdminController.cs | 109 +- .../Drivers/BooleanFieldDisplayDriver.cs | 53 +- .../ContentPickerFieldDisplayDriver.cs | 167 +- .../Drivers/DateFieldDisplayDriver.cs | 71 +- .../Drivers/DateTimeFieldDisplayDriver.cs | 97 +- .../Drivers/HtmlFieldDisplayDriver.cs | 143 +- .../Drivers/LinkFieldDisplayDriver.cs | 177 +- ...ationSetContentPickerFieldDisplayDriver.cs | 139 +- .../Drivers/MultiTextFieldDisplayDriver.cs | 93 +- .../Drivers/NumericFieldDisplayDriver.cs | 151 +- .../Drivers/TextFieldDisplayDriver.cs | 73 +- .../Drivers/TimeFieldDisplayDriver.cs | 71 +- .../Drivers/UserPickerFieldDisplayDriver.cs | 125 +- .../UserPickerFieldUserNamesDisplayDriver.cs | 23 +- .../Drivers/YoutubeFieldDisplayDriver.cs | 121 +- .../Fields/BooleanField.cs | 9 +- .../Fields/ContentPickerField.cs | 9 +- .../Fields/DateField.cs | 9 +- .../Fields/DateTimeField.cs | 9 +- .../Fields/HtmlField.cs | 9 +- .../Fields/LinkField.cs | 13 +- .../LocalizationSetContentPickerField.cs | 11 +- .../Fields/MultiTextField.cs | 9 +- .../Fields/NumericField.cs | 9 +- .../Fields/TextField.cs | 9 +- .../Fields/TimeField.cs | 9 +- .../Fields/UserNamesExtensions.cs | 45 +- .../Fields/UserPickerField.cs | 9 +- .../Fields/YoutubeField.cs | 11 +- .../GraphQL/Fields/ContentFieldsProvider.cs | 249 ++- .../Fields/ObjectGraphTypeFieldProvider.cs | 81 +- .../GraphQL/Startup.cs | 35 +- .../ContentPickerFieldQueryObjectType.cs | 45 +- .../GraphQL/Types/HtmlFieldQueryObjectType.cs | 91 +- .../GraphQL/Types/LinkFieldQueryObjectType.cs | 17 +- .../Types/UserPickerFieldQueryObjectType.cs | 69 +- .../Indexing/BooleanFieldIndexHandler.cs | 21 +- .../ContentPickerFieldIndexHandler.cs | 35 +- .../Indexing/DateFieldIndexHandler.cs | 21 +- .../Indexing/DateTimeFieldIndexHandler.cs | 21 +- .../Indexing/HtmlFieldIndexHandler.cs | 21 +- .../Indexing/LinkFieldIndexHandler.cs | 25 +- ...zationSetContentPickerFieldIndexHandler.cs | 37 +- .../Indexing/MultiTextFieldIndexHandler.cs | 23 +- .../Indexing/NumericFieldIndexHandler.cs | 31 +- .../Indexing/SQL/BooleanFieldIndexProvider.cs | 123 +- .../Indexing/SQL/ContentFieldIndexProvider.cs | 27 +- .../SQL/ContentPickerFieldIndexProvider.cs | 129 +- .../Indexing/SQL/DateFieldIndexProvider.cs | 119 +- .../SQL/DateTimeFieldIndexProvider.cs | 123 +- .../Indexing/SQL/HtmlFieldIndexProvider.cs | 123 +- .../Indexing/SQL/LinkFieldIndexProvider.cs | 147 +- .../Indexing/SQL/Migrations.cs | 1555 ++++++++------- .../SQL/MultiTextFieldIndexProvider.cs | 141 +- .../Indexing/SQL/NumericFieldIndexProvider.cs | 123 +- .../Indexing/SQL/TextFieldIndexProvider.cs | 137 +- .../Indexing/SQL/TimeFieldIndexProvider.cs | 123 +- .../SQL/UserPickerFieldIndexProvider.cs | 127 +- .../Indexing/SQL/UserPickerMigrations.cs | 147 +- .../Indexing/TextFieldIndexHandler.cs | 21 +- .../Indexing/TimeFieldIndexHandler.cs | 31 +- .../Indexing/UserPickerFieldIndexHandler.cs | 47 +- .../Indexing/YoutubeFieldIndexHandler.cs | 21 +- .../Media/MediaShapes.cs | 39 +- .../Media/Startup.cs | 13 +- .../OrchardCore.ContentFields/Migrations.cs | 139 +- .../DefaultContentPickerResultProvider.cs | 89 +- .../DefaultUserPickerResultProvider.cs | 77 +- .../Settings/BooleanFieldSettings.cs | 13 +- .../Settings/BooleanFieldSettingsDriver.cs | 35 +- .../Settings/ContentPickerFieldSettings.cs | 29 +- .../ContentPickerFieldSettingsDriver.cs | 173 +- .../Settings/DateFieldSettings.cs | 11 +- .../Settings/DateFieldSettingsDriver.cs | 33 +- .../Settings/DateTimeFieldSettings.cs | 11 +- .../Settings/DateTimeFieldSettingsDriver.cs | 33 +- .../Settings/HtmlFieldMonacoEditorSettings.cs | 9 +- .../HtmlFieldMonacoEditorSettingsDriver.cs | 81 +- .../Settings/HtmlFieldSettings.cs | 13 +- .../Settings/HtmlFieldSettingsDriver.cs | 39 +- .../HtmlFieldTrumbowygEditorSettings.cs | 11 +- .../HtmlFieldTrumbowygEditorSettingsDriver.cs | 83 +- .../Settings/LinkFieldSettings.cs | 53 +- .../Settings/LinkFieldSettingsDriver.cs | 47 +- ...calizationSetContentPickerFieldSettings.cs | 17 +- ...tionSetContentPickerFieldSettingsDriver.cs | 39 +- .../Settings/MultiTextFieldSettings.cs | 31 +- .../Settings/MultiTextFieldSettingsDriver.cs | 69 +- .../Settings/NumericFieldSettings.cs | 21 +- .../Settings/NumericFieldSettingsDriver.cs | 43 +- .../TextFieldHeaderDisplaySettings.cs | 9 +- .../TextFieldHeaderDisplaySettingsDriver.cs | 39 +- .../Settings/TextFieldMonacoEditorSettings.cs | 9 +- .../TextFieldMonacoEditorSettingsDriver.cs | 71 +- .../TextFieldPredefinedListEditorSettings.cs | 55 +- ...FieldPredefinedListEditorSettingsDriver.cs | 77 +- .../Settings/TextFieldSettings.cs | 13 +- .../Settings/TextFieldSettingsDriver.cs | 35 +- .../Settings/TimeFieldSettings.cs | 13 +- .../Settings/TimeFieldSettingsDriver.cs | 35 +- .../Settings/UserPickerFieldSettings.cs | 19 +- .../Settings/UserPickerFieldSettingsDriver.cs | 103 +- .../UserPickerFieldSettingsViewModel.cs | 27 +- .../Settings/YoutubeFieldSettings.cs | 17 +- .../Settings/YoutubeFieldSettingsDriver.cs | 33 +- .../OrchardCore.ContentFields/Startup.cs | 349 ++-- .../DisplayBooleanFieldViewModel.cs | 15 +- .../DisplayContentPickerFieldViewModel.cs | 15 +- .../ViewModels/DisplayDateFieldViewModel.cs | 15 +- .../DisplayDateTimeFieldViewModel.cs | 17 +- .../ViewModels/DisplayHtmlFieldViewModel.cs | 15 +- .../ViewModels/DisplayLinkFieldViewModel.cs | 19 +- ...alizationSetContentPickerFieldViewModel.cs | 15 +- .../DisplayMultiTextFieldViewModel.cs | 15 +- .../DisplayNumericFieldViewModel.cs | 15 +- .../ViewModels/DisplayTextFieldViewModel.cs | 15 +- .../ViewModels/DisplayTimeFieldViewModel.cs | 15 +- ...isplayUserPickerFieldUserNamesViewModel.cs | 17 +- .../DisplayUserPickerFieldViewModel.cs | 15 +- .../ViewModels/EditBooleanFieldViewModel.cs | 15 +- .../EditContentPickerFieldViewModel.cs | 19 +- .../ViewModels/EditDateFieldViewModel.cs | 15 +- .../ViewModels/EditDateTimeFieldViewModel.cs | 15 +- .../ViewModels/EditHtmlFieldViewModel.cs | 15 +- .../ViewModels/EditLinkFieldViewModel.cs | 19 +- ...alizationSetContentPickerFieldViewModel.cs | 19 +- .../ViewModels/EditMultiTextFieldViewModel.cs | 21 +- .../ViewModels/EditNumericFieldViewModel.cs | 15 +- .../ViewModels/EditTextFieldViewModel.cs | 15 +- .../ViewModels/EditTimeFieldViewModel.cs | 15 +- .../EditUserPickerFieldViewModel.cs | 41 +- .../ViewModels/EditYoutubeFieldViewModel.cs | 19 +- .../ViewModels/HeaderSettingsViewModel.cs | 9 +- .../ViewModels/HtmlSettingsViewModel.cs | 11 +- .../ViewModels/MonacoSettingsViewModel.cs | 11 +- .../MultiTextFieldSettingsViewModel.cs | 13 +- .../PredefinedListSettingsViewModel.cs | 13 +- .../ViewModels/TrumbowygSettingsViewModel.cs | 11 +- .../ViewModels/VueMultiselectItemViewModel.cs | 19 +- .../YoutubeFieldDisplayViewModel.cs | 17 +- .../AdminMenu.cs | 85 +- .../ContentRequestCultureProvider.cs | 39 +- .../Controllers/AdminController.cs | 127 +- .../ContentCulturePickerController.cs | 101 +- .../CulturePickerOptions.cs | 9 +- .../DefaultContentLocalizationManager.cs | 273 ++- .../ContentCulturePickerSettingsDriver.cs | 75 +- ...entRequestCultureProviderSettingsDriver.cs | 73 +- ...alizationContentsAdminListDisplayDriver.cs | 99 +- .../Drivers/LocalizationPartDisplayDriver.cs | 143 +- .../GraphQL/LocalizationInputObjectType.cs | 17 +- .../LocalizationPartIndexAliasProvider.cs | 29 +- .../GraphQL/LocalizationQueryObjectType.cs | 47 +- .../GraphQL/Startup.cs | 19 +- ...ntentLocalizationPartHandlerCoordinator.cs | 93 +- .../Handlers/LocalizationPartHandler.cs | 131 +- .../Indexing/LocalizationPartIndexHandler.cs | 23 +- .../Liquid/ContentLocalizationFilter.cs | 47 +- .../Liquid/SwitchCultureUrlFilter.cs | 51 +- .../Migrations.cs | 141 +- .../Models/ContentCulturePickerSettings.cs | 11 +- .../ContentRequestCultureProviderSettings.cs | 9 +- .../LocalizeContentAuthorizationHandler.cs | 103 +- .../ServiceCollectionExtensions.cs | 29 +- .../Services/ContentCulturePickerService.cs | 159 +- .../Services/IContentCulturePickerService.cs | 45 +- .../Services/ILocalizationEntries.cs | 13 +- .../Services/LocalizationEntries.cs | 309 ++- ...LocalizationPartContentsAdminListFilter.cs | 43 +- ...tionPartContentsAdminListFilterProvider.cs | 45 +- .../Services/LocalizationStateDocument.cs | 7 +- .../LocalizedContentItemsQueryProvider.cs | 169 +- ...emapUrlHrefLangExtendedMetadataProvider.cs | 83 +- .../Startup.cs | 101 +- ...ocalizationContentsAdminFilterViewModel.cs | 15 +- .../ViewModels/LocalizationPartViewModel.cs | 35 +- .../Controllers/PreviewController.cs | 301 ++- .../Drivers/ContentPreviewDriver.cs | 13 +- .../Handlers/PreviewPartHandler.cs | 75 +- .../OrchardCore.ContentPreview/Migrations.cs | 29 +- .../Models/PreviewPart.cs | 15 +- .../Models/PreviewPartSettings.cs | 15 +- .../PreviewStartupFilter.cs | 67 +- .../ResourceManagementOptionsConfiguration.cs | 33 +- .../PreviewPartSettingsDisplayDriver.cs | 73 +- .../OrchardCore.ContentPreview/Startup.cs | 25 +- .../PreviewPartSettingsViewModel.cs | 11 +- .../ViewModels/PreviewPartViewModel.cs | 17 +- .../OrchardCore.ContentTypes/AdminMenu.cs | 59 +- .../Controllers/AdminController.cs | 1457 +++++++------- .../ContentDefinitionDeploymentSource.cs | 59 +- .../ContentDefinitionDeploymentStep.cs | 25 +- .../ContentDefinitionDeploymentStepDriver.cs | 79 +- ...DeleteContentDefinitionDeploymentSource.cs | 29 +- .../DeleteContentDefinitionDeploymentStep.cs | 23 +- ...teContentDefinitionDeploymentStepDriver.cs | 55 +- ...eplaceContentDefinitionDeploymentSource.cs | 59 +- .../ReplaceContentDefinitionDeploymentStep.cs | 19 +- ...ceContentDefinitionDeploymentStepDriver.cs | 79 +- .../ContentDefinitionDisplayCoordinator.cs | 169 +- .../ContentPartSettingsDisplayDriver.cs | 45 +- .../ContentTypePartSettingsDisplayDriver.cs | 11 +- .../ContentTypeSettingsDisplayDriver.cs | 175 +- .../DefaultContentDefinitionDisplayManager.cs | 355 ++-- .../DefaultContentTypeDisplayDriver.cs | 51 +- .../GraphQLContentTypePartSettingsDriver.cs | 63 +- .../GraphQL/Startup.cs | 15 +- ...GraphQLContentTypePartSettingsViewModel.cs | 13 +- .../GraphQLContentTypeSettingsViewModel.cs | 17 +- .../RecipeSteps/ContentDefinitionStep.cs | 145 +- .../DeleteContentDefinitionStep.cs | 59 +- .../ReplaceContentDefinitionStep.cs | 117 +- .../Recipes/LuceneRecipeEventHandler.cs | 101 +- .../Services/ContentDefinitionService.cs | 765 ++++---- .../Services/StereotypeService.cs | 65 +- .../OrchardCore.ContentTypes/Startup.cs | 59 +- .../ViewModels/AddFieldViewModel.cs | 59 +- .../ViewModels/AddPartsViewModel.cs | 55 +- .../ContentDefinitionStepViewModel.cs | 13 +- .../ContentPartSettingsViewModel.cs | 19 +- .../ContentTypeSettingsViewModel.cs | 25 +- .../ViewModels/ContentTypeViewModel.cs | 11 +- .../ViewModels/CreatePartViewModel.cs | 9 +- .../ViewModels/CreateTypeViewModel.cs | 11 +- .../DeleteContentDefinitionStepViewModel.cs | 11 +- .../ViewModels/EditFieldViewModel.cs | 49 +- .../ViewModels/EditPartViewModel.cs | 69 +- .../ViewModels/EditTypePartViewModel.cs | 61 +- .../ViewModels/EditTypeViewModel.cs | 47 +- .../ViewModels/ListContentPartsViewModel.cs | 11 +- .../ViewModels/ListContentTypesViewModel.cs | 11 +- .../ReplaceContentDefinitionStepViewModel.cs | 7 +- .../OrchardCore.Contents/AdminMenu.cs | 153 +- .../AdminNodes/ContentTypesAdminNode.cs | 23 +- .../AdminNodes/ContentTypesAdminNodeDriver.cs | 137 +- .../ContentTypesAdminNodeNavigationBuilder.cs | 187 +- .../ContentTypesAdminNodeViewModel.cs | 25 +- .../AuditTrailContentController.cs | 177 +- .../AuditTrailContentEventDisplayDriver.cs | 177 +- .../Drivers/AuditTrailContentsDriver.cs | 39 +- .../Drivers/AuditTrailPartDisplayDriver.cs | 37 +- .../AuditTrailPartSettingsDisplayDriver.cs | 51 +- .../ContentAuditTrailSettingsDisplayDriver.cs | 71 +- .../ContentOrchardHelperExtensions.cs | 49 +- .../Handlers/AuditTrailContentHandler.cs | 137 +- .../AuditTrail/Migrations.cs | 31 +- .../Models/AuditTrailContentEvent.cs | 15 +- .../AuditTrail/Models/AuditTrailPart.cs | 11 +- .../ContentAuditTrailEventConfiguration.cs | 43 +- .../Services/ContentAuditTrailEventHandler.cs | 31 +- .../Settings/AuditTrailPartSettings.cs | 11 +- .../Settings/ContentAuditTrailSettings.cs | 9 +- .../AuditTrail/Startup.cs | 31 +- .../AuditTrailContentEventDetailViewModel.cs | 13 +- .../AuditTrailContentEventViewModel.cs | 29 +- .../AuditTrailPartSettingsViewModel.cs | 9 +- .../ViewModels/AuditTrailPartViewModel.cs | 9 +- .../ContentAuditTrailSettingsViewModel.cs | 9 +- .../Controllers/AdminController.cs | 1197 ++++++------ .../Controllers/ItemController.cs | 89 +- .../AddToDeploymentPlanContentDriver.cs | 37 +- ...ymentPlanContentsAdminListDisplayDriver.cs | 33 +- .../AddToDeploymentPlanController.cs | 191 +- .../AddToDeploymentPlanStartup.cs | 17 +- .../ContentItemDeploymentSource.cs | 67 +- .../ContentItemDeploymentStep.cs | 21 +- .../ContentItemDeploymentStepDriver.cs | 77 +- .../ContentItemDeploymentStepViewModel.cs | 11 +- .../Deployment/AllContentDeploymentSource.cs | 71 +- .../Deployment/AllContentDeploymentStep.cs | 21 +- .../AllContentDeploymentStepDriver.cs | 41 +- .../Deployment/ContentDeploymentSource.cs | 79 +- .../Deployment/ContentDeploymentStep.cs | 23 +- .../Deployment/ContentDeploymentStepDriver.cs | 47 +- .../DisplayJsonContentItemViewModel.cs | 11 +- .../Download/DownloadContentDriver.cs | 37 +- .../Deployment/Download/DownloadController.cs | 113 +- .../Deployment/Download/DownloadStartup.cs | 13 +- ...xportContentToDeploymentTargetAdminMenu.cs | 57 +- ...tContentToDeploymentTargetContentDriver.cs | 83 +- ...entTargetContentsAdminListDisplayDriver.cs | 47 +- ...ntentToDeploymentTargetDeploymentSource.cs | 101 +- ...ContentToDeploymentTargetDeploymentStep.cs | 17 +- ...tToDeploymentTargetDeploymentStepDriver.cs | 27 +- ...portContentToDeploymentTargetMigrations.cs | 57 +- ...ExportContentToDeploymentTargetSettings.cs | 15 +- ...ToDeploymentTargetSettingsDisplayDriver.cs | 65 +- ...tentToDeploymentTargetSettingsViewModel.cs | 9 +- .../ExportContentToDeploymentTargetStartup.cs | 25 +- .../Drivers/ContentOptionsDisplayDriver.cs | 109 +- .../Drivers/ContentsDriver.cs | 153 +- .../Drivers/DateEditorDriver.cs | 71 +- .../Drivers/OwnerEditorDriver.cs | 117 +- .../Feeds/CommonFeedItemBuilder.cs | 109 +- .../OrchardCore.Contents/GraphQL/Startup.cs | 13 +- .../Handlers/ContentsHandler.cs | 121 +- .../Handlers/FullTextAspectContentHandler.cs | 93 +- .../Indexing/AspectsContentIndexHandler.cs | 61 +- .../Indexing/ContentItemIndexCoordinator.cs | 113 +- .../Indexing/DefaultContentIndexHandler.cs | 93 +- .../Indexing/FullTextContentIndexHandler.cs | 39 +- .../Liquid/BuildDisplayFilter.cs | 99 +- .../Liquid/ContentAnchorTag.cs | 303 ++- .../Liquid/ContentItemFilter.cs | 35 +- .../Liquid/ContentItemRecursionHelper.cs | 51 +- .../Liquid/ContentItemTag.cs | 21 +- .../Liquid/DisplayTextFilter.cs | 21 +- .../Liquid/DisplayUrlFilter.cs | 65 +- .../Liquid/FullTextFilter.cs | 119 +- .../OrchardCore.Contents/Migrations.cs | 29 +- .../OrchardCore.Contents/Models/CommonPart.cs | 15 +- .../Models/CommonPartSettings.cs | 11 +- .../Models/FullTextAspectSettings.cs | 43 +- .../Recipes/ContentStep.cs | 61 +- .../Scripting/ContentMethodsProvider.cs | 119 +- .../OrchardCore.Contents/Scripting/Startup.cs | 15 +- .../Scripting/UrlMethodsProvider.cs | 65 +- .../ContentTypeAuthorizationHandler.cs | 129 +- .../Services/ContentItemIdHandleProvider.cs | 23 +- .../Services/ContentTypeFilterNode.cs | 25 +- .../DefaultContentsAdminListFilterParser.cs | 21 +- .../DefaultContentsAdminListFilterProvider.cs | 385 ++-- .../CommonPartSettingsDisplayDriver.cs | 41 +- .../FullTextAspectSettingsDisplayDriver.cs | 95 +- .../OrchardCore.Contents/Shapes.cs | 127 +- .../ContentTypesSitemapSourceBuilder.cs | 235 ++- .../ContentTypesSitemapSourceDriver.cs | 193 +- ...tTypesSitemapSourceModifiedDateProvider.cs | 87 +- .../ContentTypesSitemapSourceUpdateHandler.cs | 91 +- .../ContentTypesSitemapSourceViewModel.cs | 59 +- .../ContentTypesSitemapUpdateHandler.cs | 33 +- .../DefaultContentItemsQueryProvider.cs | 93 +- .../OrchardCore.Contents/Startup.cs | 381 ++-- .../DisplayContentItemViewComponent.cs | 55 +- .../SelectContentTypesViewComponent.cs | 47 +- .../AllContentDeploymentStepViewModel.cs | 9 +- .../ViewModels/CommonPartSettingsViewModel.cs | 11 +- .../ContentDeploymentStepViewModel.cs | 11 +- .../ViewModels/DateEditorViewModel.cs | 9 +- .../FullTextAspectSettingsViewModel.cs | 15 +- .../ViewModels/IndexingEditorViewModel.cs | 9 +- .../ViewModels/ListContentTypesViewModel.cs | 9 +- .../ViewModels/ListContentsViewModel.cs | 21 +- .../ViewModels/OwnerEditorViewModel.cs | 9 +- .../ViewModels/SelectContentTypesViewModel.cs | 45 +- .../Workflows/Activities/ContentActivity.cs | 259 ++- .../Activities/ContentCreatedEvent.cs | 23 +- .../Activities/ContentDeletedEvent.cs | 23 +- .../Activities/ContentDraftSavedEvent.cs | 23 +- .../Workflows/Activities/ContentEvent.cs | 65 +- .../Activities/ContentPublishedEvent.cs | 23 +- .../Workflows/Activities/ContentTask.cs | 17 +- .../Activities/ContentUnpublishedEvent.cs | 23 +- .../Activities/ContentUpdatedEvent.cs | 23 +- .../Activities/ContentVersionedEvent.cs | 21 +- .../Workflows/Activities/CreateContentTask.cs | 175 +- .../Workflows/Activities/DeleteContentTask.cs | 73 +- .../Activities/PublishContentTask.cs | 75 +- .../Activities/RetrieveContentTask.cs | 61 +- .../Activities/UnpublishContentTask.cs | 75 +- .../Workflows/Activities/UpdateContentTask.cs | 227 ++- .../ContentCreatedEventDisplayDriver.cs | 11 +- .../ContentDeletedEventDisplayDriver.cs | 11 +- .../ContentDraftSavedEventDisplayDriver.cs | 11 +- .../Drivers/ContentEventDisplayDriver.cs | 79 +- .../ContentPublishedEventDisplayDriver.cs | 11 +- .../Drivers/ContentTaskDisplayDriver.cs | 17 +- .../ContentUnpublishedEventDisplayDriver.cs | 11 +- .../ContentUpdatedEventDisplayDriver.cs | 11 +- .../ContentVersionedEventDisplayDriver.cs | 11 +- .../Drivers/CreateContentTaskDisplayDriver.cs | 45 +- .../Drivers/DeleteContentTaskDisplayDriver.cs | 19 +- .../PublishContentTaskDisplayDriver.cs | 19 +- .../RetrieveContentTaskDisplayDriver.cs | 19 +- .../UnpublishContentTaskDisplayDriver.cs | 19 +- .../Drivers/UpdateContentTaskDisplayDriver.cs | 45 +- .../Handlers/ContentItemSerializer.cs | 51 +- .../Workflows/Handlers/ContentsHandler.cs | 99 +- .../OrchardCore.Contents/Workflows/Startup.cs | 41 +- .../ContentCreatedEventViewModel.cs | 7 +- .../ContentDeletedEventViewModel.cs | 7 +- .../ContentDraftSavedEventViewModel.cs | 7 +- .../ViewModels/ContentEventViewModel.cs | 23 +- .../ContentPublishedEventViewModel.cs | 7 +- .../ViewModels/ContentTaskViewModel.cs | 17 +- .../ContentUnpublishedEventViewModel.cs | 7 +- .../ContentUpdatedEventViewModel.cs | 7 +- .../ContentVersionedEventViewModel.cs | 7 +- .../ViewModels/CreateContentTaskViewModel.cs | 17 +- .../ViewModels/DeleteContentTaskViewModel.cs | 15 +- .../ViewModels/PublishContentTaskViewModel.cs | 15 +- .../RetrieveContentTaskViewModel.cs | 15 +- .../UnpublishContentTaskViewModel.cs | 15 +- .../ViewModels/UpdateContentTaskViewModel.cs | 19 +- .../OrchardCore.Cors/AdminMenu.cs | 49 +- .../Controllers/AdminController.cs | 207 +- .../Services/CorsOptionsConfiguration.cs | 117 +- .../OrchardCore.Cors/Services/CorsService.cs | 35 +- .../Settings/CorsPolicySetting.cs | 27 +- .../OrchardCore.Cors/Settings/CorsSettings.cs | 9 +- .../OrchardCore.Cors/Startup.cs | 33 +- .../ViewModels/CorsPolicyViewModel.cs | 27 +- .../ViewModels/CorsSettingsViewModel.cs | 11 +- .../OrchardCore.CustomSettings/AdminMenu.cs | 79 +- .../CustomSettingsDeploymentSource.cs | 63 +- .../CustomSettingsDeploymentStep.cs | 17 +- .../CustomSettingsDeploymentStepDriver.cs | 73 +- .../Drivers/CustomSettingsDisplayDriver.cs | 93 +- .../Recipes/CustomSettingsStep.cs | 49 +- .../CustomSettingsAuthorizationHandler.cs | 57 +- .../Services/CustomSettingsService.cs | 175 +- .../OrchardCore.CustomSettings/Startup.cs | 53 +- .../CustomSettingsDeploymentStepViewModel.cs | 13 +- .../ViewModels/CustomSettingsEditViewModel.cs | 9 +- .../OrchardCore.Demo/AdminMenu.cs | 85 +- .../OrchardCore.Demo/Commands/DemoCommands.cs | 31 +- .../Components/FakeViewComponent.cs | 11 +- .../TestContentElementDisplayDriver.cs | 119 +- .../Controllers/AdminController.cs | 11 +- .../Controllers/ContentApiController.cs | 91 +- .../Controllers/ContentController.cs | 137 +- .../Controllers/DemoController.cs | 35 +- .../Controllers/HomeController.cs | 213 +- .../Controllers/TodoController.cs | 157 +- .../Drivers/UserProfileDisplayDriver.cs | 63 +- .../OrchardCore.Demo/GraphQL/Startup.cs | 135 +- .../GraphQL/TestQueryObjectType.cs | 17 +- .../Middlewares/BlockingMiddleware.cs | 29 +- .../Middlewares/NonBlockingMiddleware.cs | 27 +- .../OrchardCore.Demo/Migrations.cs | 31 +- .../Models/CustomViewModel.cs | 9 +- .../OrchardCore.Demo/Models/FakeShape.cs | 9 +- .../OrchardCore.Demo/Models/HomeViewModel.cs | 11 +- .../Models/TestContentField.cs | 9 +- .../Models/TestContentPartA.cs | 11 +- .../Models/TestContentPartAShape.cs | 9 +- .../OrchardCore.Demo/Models/TodoModel.cs | 15 +- .../OrchardCore.Demo/Models/UserProfile.cs | 15 +- .../Pages/Foo/Admin/Edit.cshtml.cs | 127 +- .../OrchardCore.Demo/Pages/Foo/List.cshtml.cs | 85 +- .../Services/TestBackgroundTask.cs | 17 +- .../Services/TestDependency.cs | 27 +- .../Services/UserProfileClaimsProvider.cs | 69 +- .../OrchardCore.Demo/Shapes/BazTagHelper.cs | 23 +- .../Shapes/DemoShapeProvider.cs | 37 +- .../OrchardCore.Demo/Startup.cs | 179 +- .../TagHelpers/BazTagHelper.cs | 15 +- .../ViewModels/EditUserProfileViewModel.cs | 13 +- .../ViewModels/TodoViewModel.cs | 55 +- .../AdminMenu.cs | 55 +- .../ExportRemoteInstanceController.cs | 171 +- .../ImportRemoteInstanceController.cs | 155 +- .../Controllers/RemoteClientController.cs | 383 ++-- .../Controllers/RemoteInstanceController.cs | 407 ++-- .../Models/RemoteClient.cs | 13 +- .../Models/RemoteClientList.cs | 9 +- .../Models/RemoteInstance.cs | 17 +- .../Models/RemoteInstanceList.cs | 9 +- .../Services/RemoteClientService.cs | 125 +- .../RemoteInstanceDeploymentTargetProvider.cs | 57 +- .../Services/RemoteInstanceService.cs | 113 +- .../OrchardCore.Deployment.Remote/Startup.cs | 21 +- .../ViewModels/EditRemoteClientViewModel.cs | 13 +- .../ViewModels/EditRemoteInstanceViewModel.cs | 17 +- .../ViewModels/ImportViewModel.cs | 13 +- .../ViewModels/RemoteClientIndexViewModel.cs | 43 +- .../RemoteInstanceIndexViewModel.cs | 15 +- .../OrchardCore.Deployment/AdminMenu.cs | 65 +- .../Controllers/DeploymentPlanController.cs | 473 +++-- .../Controllers/ExportFileController.cs | 99 +- .../Controllers/ImportController.cs | 261 ++- .../Controllers/StepController.cs | 369 ++-- .../DeploymentPlanDeploymentSource.cs | 109 +- .../DeploymentPlanDeploymentStep.cs | 23 +- .../DeploymentPlanDeploymentStepDriver.cs | 73 +- .../DeploymentPlanService.cs | 155 +- .../Indexes/DeploymentPlanIndex.cs | 33 +- .../OrchardCore.Deployment/Migrations.cs | 17 +- .../Recipes/DeploymentPlansRecipeStep.cs | 135 +- .../FileDownloadDeploymentTargetProvider.cs | 49 +- .../OrchardCore.Deployment/Startup.cs | 37 +- .../Steps/CustomFileDeploymentSource.cs | 17 +- .../Steps/CustomFileDeploymentStep.cs | 25 +- .../Steps/CustomFileDeploymentStepDriver.cs | 43 +- .../Steps/JsonRecipeDeploymentSource.cs | 19 +- .../Steps/JsonRecipeDeploymentStep.cs | 21 +- .../Steps/JsonRecipeDeploymentStepDriver.cs | 89 +- .../Steps/RecipeFileDeploymentStep.cs | 37 +- .../Steps/RecipeFileDeploymentStepDriver.cs | 75 +- .../CreateDeploymentPlanViewModel.cs | 11 +- .../CustomFileDeploymentStepViewModel.cs | 11 +- .../DeploymentPlanDeploymentStepViewModel.cs | 13 +- .../DeploymentPlanIndexViewModel.cs | 47 +- .../DisplayDeploymentPlanViewModel.cs | 13 +- .../EditDeploymentPlanStepViewModel.cs | 19 +- .../ViewModels/EditDeploymentPlanViewModel.cs | 13 +- .../ViewModels/ImportJsonViewModel.cs | 9 +- .../JsonRecipeDeploymentStepViewModel.cs | 13 +- .../RecipeFileDeploymentStepViewModel.cs | 25 +- .../DiagnosticsStartupFilter.cs | 59 +- .../OrchardCore.Diagnostics/Startup.cs | 29 +- .../CacheContextEntryExtensions.cs | 21 +- .../CacheOptionsConfiguration.cs | 61 +- .../CachedShapeWrapperShapes.cs | 45 +- .../DynamicCacheShapeDisplayEvents.cs | 175 +- .../Models/CacheContextModel.cs | 85 +- .../Services/DefaultDynamicCache.cs | 39 +- .../Services/DefaultDynamicCacheService.cs | 289 ++- .../OrchardCore.DynamicCache/Startup.cs | 47 +- .../TagHelpers/CacheDependencyTagHelper.cs | 55 +- .../TagHelpers/DynamicCacheTagHelper.cs | 431 ++-- .../DynamicCacheTagHelperService.cs | 9 +- .../Models/DefaultAzureEmailOptions.cs | 2 +- .../OrchardCore.Email/AdminMenu.cs | 73 +- .../OrchardCoreBuilderExtensions.cs | 27 +- .../OrchardCore.Email/Startup.cs | 19 +- .../ViewModels/EmailSettingsBaseViewModel.cs | 2 +- .../Workflows/Activities/EmailTask.cs | 213 +- .../Drivers/EmailTaskDisplayDriver.cs | 51 +- .../OrchardCore.Email/Workflows/Startup.cs | 13 +- .../ViewModels/EmailTaskViewModel.cs | 25 +- .../FacebookLoginDeploymentSource.cs | 41 +- .../Deployment/FacebookLoginDeploymentStep.cs | 17 +- .../FacebookLoginDeploymentStepDriver.cs | 27 +- .../Drivers/FacebookSettingsDisplayDriver.cs | 149 +- .../OrchardCoreBuilderExtensions.cs | 19 +- .../Filters/FBInitFilter.cs | 47 +- .../FacebookLoginConfiguration.cs | 173 +- .../FacebookLoginSettingsDisplayDriver.cs | 81 +- .../Recipes/FacebookLoginSettingsStep.cs | 47 +- .../Login/Services/FacebookLoginService.cs | 67 +- .../Login/Services/IFacebookLoginService.cs | 15 +- .../Login/Settings/FacebookLoginSettings.cs | 11 +- .../FacebookLoginSettingsViewModel.cs | 13 +- .../Recipes/FacebookSettingsStep.cs | 71 +- .../ResourceManagementOptionsConfiguration.cs | 33 +- .../OrchardCore.Facebook/ScriptsMiddleware.cs | 73 +- .../Services/FacebookService.cs | 77 +- .../Services/IFacebookService.cs | 13 +- .../Settings/FacebookSettings.cs | 19 +- .../OrchardCore.Facebook/Startup.cs | 39 +- .../OrchardCore.Facebook/StartupLogin.cs | 51 +- .../OrchardCore.Facebook/StartupWidgets.cs | 23 +- .../ViewModels/ErrorViewModel.cs | 11 +- .../ViewModels/FacebookSettingsViewModel.cs | 29 +- .../FacebookPluginPartDisplayDriver.cs | 123 +- .../Handlers/FacebookPluginPartHandler.cs | 57 +- .../Widgets/Models/FacebookPluginPart.cs | 9 +- .../Settings/FacebookPluginPartSettings.cs | 9 +- ...FacebookPluginPartSettingsDisplayDriver.cs | 63 +- .../FacebookPluginPartSettingsViewModel.cs | 13 +- .../ViewModels/FacebookPluginPartViewModel.cs | 23 +- .../Widgets/WidgetMigrations.cs | 39 +- .../OrchardCore.Features/AdminMenu.cs | 55 +- .../Controllers/AdminController.cs | 335 ++-- .../Deployment/AllFeaturesDeploymentSource.cs | 45 +- .../Deployment/AllFeaturesDeploymentStep.cs | 21 +- .../AllFeaturesDeploymentStepDriver.cs | 41 +- .../Models/ModuleEntry.cs | 61 +- .../Recipes/Executors/FeatureStep.cs | 57 +- .../Services/IModuleService.cs | 61 +- .../Services/ModuleService.cs | 271 ++- .../OrchardCore.Features/Startup.cs | 25 +- .../AllFeaturesDeploymentStepViewModel.cs | 9 +- .../ViewModels/FeaturesViewModel.cs | 19 +- .../ViewModels/ModulesIndexViewModel.cs | 23 +- .../Controllers/FeedController.cs | 117 +- .../OrchardCore.Feeds/Startup.cs | 11 +- .../Controllers/AdminController.cs | 197 +- .../Drivers/BagPartDisplayDriver.cs | 367 ++-- .../Drivers/FlowMetadataDisplayDriver.cs | 47 +- .../Drivers/FlowPartDisplayDriver.cs | 191 +- .../GraphQL/BagPartQueryObjectType.cs | 21 +- .../GraphQL/FlowAlignmentEnum.cs | 21 +- .../GraphQL/FlowMetadataContentTypeBuilder.cs | 19 +- .../GraphQL/FlowMetadataQueryObjectType.cs | 15 +- .../GraphQL/FlowPartQueryObjectType.cs | 19 +- .../OrchardCore.Flows/GraphQL/Startup.cs | 19 +- .../Handlers/BagPartHandler.cs | 25 +- .../Indexing/BagPartIndexHandler.cs | 55 +- .../Indexing/FlowPartIndexHandler.cs | 55 +- .../OrchardCore.Flows/Migrations.cs | 85 +- .../OrchardCore.Flows/Models/BagPart.cs | 11 +- .../Models/BagPartSettings.cs | 13 +- .../OrchardCore.Flows/Models/FlowMetadata.cs | 25 +- .../OrchardCore.Flows/Models/FlowPart.cs | 11 +- .../Models/FlowPartSettings.cs | 9 +- .../Settings/BagPartSettingsDisplayDriver.cs | 149 +- .../Settings/FlowPartSettingsDisplayDriver.cs | 61 +- .../OrchardCore.Flows/Startup.cs | 63 +- .../ViewModels/BagPartEditViewModel.cs | 35 +- .../ViewModels/BagPartSettingsViewModel.cs | 19 +- .../ViewModels/BagPartViewModel.cs | 21 +- .../ViewModels/BagPartWidgetViewModel.cs | 17 +- .../ViewModels/BuildEditorViewModel.cs | 9 +- .../ViewModels/FlowPartEditViewModel.cs | 35 +- .../ViewModels/FlowPartSettingsViewModel.cs | 13 +- .../ViewModels/FlowPartViewModel.cs | 15 +- .../Drivers/ButtonPartDisplayDriver.cs | 43 +- .../Drivers/FormContentDisplayDriver.cs | 27 +- .../Drivers/FormElementPartDisplayDriver.cs | 29 +- .../FormInputElementPartDisplayDriver.cs | 51 +- .../Drivers/FormPartDisplayDriver.cs | 49 +- .../Drivers/InputPartDisplayDriver.cs | 45 +- .../Drivers/LabelPartDisplayDriver.cs | 37 +- .../Drivers/SelectPartDisplayDriver.cs | 71 +- .../Drivers/TextAreaPartDisplayDriver.cs | 41 +- .../Drivers/ValidationPartDisplayDriver.cs | 37 +- .../ValidationSummaryPartDisplayDriver.cs | 39 +- .../Filters/ExportModelStateAttribute.cs | 47 +- .../Filters/ImportModelStateAttribute.cs | 39 +- .../Filters/ImportModelStatePageFilter.cs | 49 +- .../Filters/ModelStateTransferAttribute.cs | 9 +- .../GraphQL/ButtonPartQueryObjectType.cs | 15 +- .../GraphQL/FormElementPartQueryObjectType.cs | 13 +- .../FormInputElementPartQueryObjectType.cs | 13 +- .../GraphQL/FormPartQueryObjectType.cs | 17 +- .../GraphQL/InputPartQueryObjectType.cs | 17 +- .../GraphQL/LabelPartQueryObjectType.cs | 15 +- .../OrchardCore.Forms/GraphQL/Startup.cs | 31 +- .../GraphQL/TextAreaPartQueryObjectType.cs | 15 +- .../GraphQL/ValidationPartQueryObjectType.cs | 13 +- .../ValidationSummaryPartQueryObjectType.cs | 11 +- .../Helpers/ModelStateHelpers.cs | 61 +- .../OrchardCore.Forms/Migrations.cs | 523 +++-- .../OrchardCore.Forms/Models/ButtonPart.cs | 11 +- .../Models/FormElementPart.cs | 15 +- .../Models/FormInputElementPart.cs | 15 +- .../OrchardCore.Forms/Models/FormPart.cs | 23 +- .../OrchardCore.Forms/Models/InputPart.cs | 13 +- .../OrchardCore.Forms/Models/LabelPart.cs | 9 +- .../OrchardCore.Forms/Models/SelectPart.cs | 35 +- .../OrchardCore.Forms/Models/TextAreaPart.cs | 11 +- .../Models/ValidationPart.cs | 9 +- .../Models/ValidationSummaryPart.cs | 9 +- .../OrchardCore.Forms/Startup.cs | 109 +- .../ViewModels/ButtonPartEditViewModel.cs | 13 +- .../FormElementPartEditViewModel.cs | 9 +- .../FormInputElementPartEditViewModel.cs | 9 +- .../ViewModels/FormPartEditViewModel.cs | 19 +- .../ViewModels/InputPartEditViewModel.cs | 13 +- .../ViewModels/LabelPartEditViewModel.cs | 9 +- .../ViewModels/SelectPartEditViewModel.cs | 13 +- .../ViewModels/TextAreaPartEditViewModel.cs | 11 +- .../ViewModels/ValidationPartEditViewModel.cs | 11 +- .../Activities/AddModelValidationErrorTask.cs | 85 +- .../Activities/BindModelStateTask.cs | 65 +- .../ValidateAntiforgeryTokenTask.cs | 61 +- .../Activities/ValidateFormFieldTask.cs | 89 +- .../Workflows/Activities/ValidateFormTask.cs | 67 +- ...ddModelValidationErrorTaskDisplayDriver.cs | 23 +- .../BindModelStateTaskDisplayDriver.cs | 7 +- ...lidateAntiforgeryTokenTaskDisplayDriver.cs | 7 +- .../ValidateFormFieldTaskDisplayDriver.cs | 23 +- .../Drivers/ValidateFormTaskDisplayDriver.cs | 7 +- .../OrchardCore.Forms/Workflows/Startup.cs | 21 +- .../AddModelValidationErrorTaskViewModel.cs | 11 +- .../ValidateFormFieldTaskViewModel.cs | 15 +- .../AdminMenuGitHubLogin.cs | 59 +- .../Configuration/GithubHandler.cs | 143 +- .../Configuration/GithubOptions.cs | 33 +- .../GithubOptionsConfiguration.cs | 115 +- ...thubAuthenticationSettingsDisplayDriver.cs | 137 +- .../OrchardCoreBuilderExtensions.cs | 19 +- .../OrchardCore.GitHub/GithubConstants.cs | 11 +- .../GithubAuthenticationSettingsStep.cs | 55 +- .../Services/GithubAuthenticationService.cs | 79 +- .../Services/IGithubAuthenticationService.cs | 15 +- .../Settings/GithubAuthenticationSettings.cs | 15 +- .../OrchardCore.GitHub/Startup.cs | 49 +- .../GithubAuthenticationSettingsViewModel.cs | 23 +- .../GoogleAnalyticsSettingsDisplayDriver.cs | 77 +- .../Analytics/GoogleAnalyticsFilter.cs | 59 +- .../Services/GoogleAnalyticsService.cs | 23 +- .../Services/IGoogleAnalyticsService.cs | 9 +- .../Settings/GoogleAnalyticsSettings.cs | 9 +- .../GoogleAnalyticsSettingsViewModel.cs | 11 +- .../GoogleOptionsConfiguration.cs | 111 +- ...ogleAuthenticationSettingsDisplayDriver.cs | 139 +- .../Services/GoogleAuthenticationService.cs | 101 +- .../Settings/GoogleAuthenticationSettings.cs | 15 +- .../GoogleAuthenticationSettingsViewModel.cs | 23 +- .../OrchardCoreBuilderExtensions.cs | 19 +- .../GoogleAuthenticationAdminMenu.cs | 173 +- .../GoogleAuthenticationStartup.cs | 131 +- .../OrchardCore.Google/GoogleConstants.cs | 15 +- .../GoogleTagManagerSettingsDisplayDriver.cs | 77 +- .../TagManager/GoogleTagManagerFilter.cs | 59 +- .../Services/GoogleTagManagerService.cs | 21 +- .../Services/IGoogleTagManagerService.cs | 9 +- .../Settings/GoogleTagManagerSettings.cs | 9 +- .../GoogleTagManagerSettingsViewModel.cs | 11 +- .../OrchardCore.HealthChecks/Startup.cs | 65 +- .../Routing/HomeRouteTransformer.cs | 33 +- .../Routing/HomeRouteValuesAddressScheme.cs | 87 +- .../OrchardCore.HomeRoute/Startup.cs | 27 +- .../Drivers/HtmlBodyPartDisplayDriver.cs | 133 +- .../GraphQL/HtmlBodyQueryObjectType.cs | 71 +- .../OrchardCore.Html/GraphQL/Startup.cs | 13 +- .../Handlers/HtmlBodyPartHandler.cs | 97 +- .../Indexing/HtmlBodyPartIndexHandler.cs | 21 +- .../OrchardCore.Html/Media/Startup.cs | 13 +- .../OrchardCore.Html/Migrations.cs | 189 +- .../OrchardCore.Html/Models/HtmlBodyPart.cs | 9 +- .../HtmlBodyMetaWeblogDriver.cs | 29 +- .../RemotePublishingStartup.cs | 13 +- .../HtmlBodyPartMonacoEditorSettings.cs | 13 +- .../HtmlBodyPartMonacoEditorSettingsDriver.cs | 75 +- .../Settings/HtmlBodyPartSettings.cs | 19 +- .../HtmlBodyPartSettingsDisplayDriver.cs | 35 +- .../HtmlBodyPartTrumbowygEditorSettings.cs | 13 +- ...mlBodyPartTrumbowygEditorSettingsDriver.cs | 83 +- .../OrchardCore.Html/Startup.cs | 29 +- .../HtmlBodyPartSettingsViewModel.cs | 9 +- .../ViewModels/HtmlBodyPartViewModel.cs | 21 +- .../ViewModels/MonacoSettingsViewModel.cs | 9 +- .../ViewModels/TrumbowygSettingsViewModel.cs | 11 +- .../OrchardCore.Https/AdminMenu.cs | 55 +- .../Drivers/HttpsSettingsDisplayDriver.cs | 125 +- .../Services/HttpsService.cs | 21 +- .../Services/IHttpsService.cs | 9 +- .../Settings/HttpsSettings.cs | 15 +- .../OrchardCore.Https/Startup.cs | 81 +- .../ViewModels/HttpsSettingsViewModel.cs | 17 +- .../CreateIndexingTaskContentHandler.cs | 51 +- .../IndexingTaskManager.cs | 265 ++- .../OrchardCore.Indexing/Migrations.cs | 29 +- .../OrchardCore.Indexing/Startup.cs | 21 +- .../OrchardCore.Layers/AdminMenu.cs | 61 +- .../Controllers/AdminController.cs | 517 +++-- .../Controllers/LayerRuleController.cs | 501 +++-- .../Deployment/AllLayersDeploymentSource.cs | 71 +- .../Deployment/AllLayersDeploymentStep.cs | 17 +- .../AllLayersDeploymentStepDriver.cs | 27 +- .../Drivers/LayerMetadataWelder.cs | 117 +- .../Drivers/LayerSiteSettingsDisplayDriver.cs | 77 +- .../GraphQL/LayerQueryObjectType.cs | 75 +- .../GraphQL/LayerWidgetQueryObjectType.cs | 35 +- .../GraphQL/SiteLayersQuery.cs | 65 +- .../OrchardCore.Layers/GraphQL/Startup.cs | 17 +- .../Handlers/LayerMetadataHandler.cs | 45 +- .../Indexes/LayerMetadataIndex.cs | 43 +- .../OrchardCore.Layers/Migrations.cs | 83 +- .../OrchardCore.Layers/Models/Layer.cs | 13 +- .../Models/LayerMetadata.cs | 15 +- .../Models/LayerSettings.cs | 9 +- .../Models/LayersDocument.cs | 9 +- .../OrchardCore.Layers/Recipes/LayerStep.cs | 201 +- .../Services/DefaultLayersMethodProvider.cs | 171 +- .../Services/ILayerService.cs | 35 +- .../Services/LayerFilter.cs | 203 +- .../Services/LayerService.cs | 89 +- .../OrchardCore.Layers/Startup.cs | 47 +- .../ViewModels/LayerEditViewModel.cs | 15 +- .../ViewModels/LayerMetadataEditViewModel.cs | 13 +- .../ViewModels/LayerRuleCreateViewModel.cs | 15 +- .../ViewModels/LayerRuleEditViewModel.cs | 17 +- .../ViewModels/LayerSettingsViewModel.cs | 9 +- .../ViewModels/LayersIndexViewModel.cs | 19 +- .../ViewModels/WidgetWrapper.cs | 15 +- .../Drivers/LiquidPartDisplayDriver.cs | 87 +- .../Filters/AbsoluteUrlFilter.cs | 33 +- .../Filters/ContentUrlFilter.cs | 25 +- .../OrchardCore.Liquid/Filters/JsonFilter.cs | 63 +- .../Filters/JsonParseFilter.cs | 19 +- .../Filters/LiquidFilter.cs | 29 +- .../Filters/LocalTimeZoneFilter.cs | 67 +- .../Filters/ShortCodeFilter.cs | 41 +- .../Filters/SlugifyFilter.cs | 25 +- .../Filters/UtcTimeZoneFilter.cs | 67 +- .../Handlers/LiquidPartHandler.cs | 55 +- .../Indexing/LiquidPartIndexHandler.cs | 21 +- .../OrchardCore.Liquid/Migrations.cs | 29 +- .../OrchardCore.Liquid/Models/LiquidPart.cs | 9 +- .../ResourceManagementOptionsConfiguration.cs | 41 +- .../OrchardCore.Liquid/ScriptsMiddleware.cs | 77 +- .../Services/LiquidTemplateManager.cs | 179 +- .../OrchardCore.Liquid/Startup.cs | 123 +- .../ViewModels/LiquidPartViewModel.cs | 19 +- .../AdminNodes/ListsAdminNode.cs | 15 +- .../AdminNodes/ListsAdminNodeDriver.cs | 91 +- .../ListsAdminNodeNavigationBuilder.cs | 181 +- .../AdminNodes/ListsAdminNodeViewModel.cs | 17 +- .../Controllers/OrderController.cs | 119 +- .../Controllers/RemotePublishingController.cs | 91 +- .../Drivers/ContainedPartDisplayDriver.cs | 261 ++- .../ListPartContentsAdminListDisplayDriver.cs | 45 +- .../Drivers/ListPartDisplayDriver.cs | 295 ++- .../Feeds/ListFeedEditViewModel.cs | 15 +- .../OrchardCore.Lists/Feeds/ListFeedQuery.cs | 149 +- .../Feeds/ListFeedQueryViewModel.cs | 9 +- .../Feeds/ListPartFeedDisplayDriver.cs | 57 +- .../Feeds/ListPartFeedHandler.cs | 21 +- .../GraphQL/ContainedInputObjectType.cs | 15 +- .../ContainedPartIndexAliasProvider.cs | 29 +- .../GraphQL/ContainedQueryObjectType.cs | 17 +- .../GraphQL/ListQueryObjectType.cs | 77 +- .../OrchardCore.Lists/GraphQL/Startup.cs | 21 +- .../Handlers/ContainedPartHandler.cs | 49 +- .../ContainedPartLocalizationHandler.cs | 85 +- .../Handlers/ListPartHandler.cs | 27 +- .../Handlers/ListPartLocalizationHandler.cs | 53 +- .../Helpers/ListQueryHelpers.cs | 31 +- .../ContainedPartContentIndexHandler.cs | 37 +- .../Indexes/ContainedPartIndex.cs | 79 +- .../Liquid/ContainerFilter.cs | 39 +- .../Liquid/ListCountFilter.cs | 39 +- .../Liquid/ListItemsFilter.cs | 39 +- .../OrchardCore.Lists/Migrations.cs | 207 +- .../Models/ContainedItemOptions.cs | 25 +- .../OrchardCore.Lists/Models/ContainedPart.cs | 33 +- .../OrchardCore.Lists/Models/ListPart.cs | 7 +- .../Models/ListPartSettings.cs | 15 +- .../RemotePublishing/ListMetaWeblogDriver.cs | 17 +- .../RemotePublishing/MetaWeblogHandler.cs | 753 ++++--- .../RemotePublishingStartup.cs | 35 +- .../Services/ContainerService.cs | 563 +++--- .../Services/IContainerService.cs | 41 +- .../ListPartContentsAdminListFilter.cs | 51 +- .../Settings/ListPartSettingsDisplayDriver.cs | 107 +- .../OrchardCore.Lists/Startup.cs | 117 +- .../ViewModels/EditContainedPartViewModel.cs | 15 +- .../ListPartContentsAdminFilterViewModel.cs | 11 +- .../ViewModels/ListPartFilterViewModel.cs | 11 +- .../ViewModels/ListPartSettingsViewModel.cs | 19 +- .../ViewModels/ListPartViewModel.cs | 21 +- .../OrchardCore.Localization/AdminMenu.cs | 79 +- .../LocalizationSettingsDisplayDriver.cs | 185 +- .../GraphQL/CultureQueryObjectType.cs | 23 +- .../GraphQL/SiteCulture.cs | 25 +- .../GraphQL/SiteCulturesQuery.cs | 97 +- .../GraphQL/Startup.cs | 23 +- .../Models/CultureEntry.cs | 33 +- .../Models/LocalizationSettings.cs | 43 +- .../ModularPoFileLocationProvider.cs | 121 +- .../Services/LocalizationService.cs | 83 +- .../OrchardCore.Localization/Startup.cs | 123 +- .../LocalizationSettingsViewModel.cs | 37 +- .../Drivers/MarkdownBodyPartDisplayDriver.cs | 155 +- .../Drivers/MarkdownFieldDisplayDriver.cs | 151 +- .../Fields/MarkdownField.cs | 9 +- .../Filters/Markdownify.cs | 23 +- .../GraphQL/MarkdownBodyQueryObjectType.cs | 105 +- .../GraphQL/MarkdownFieldQueryObjectType.cs | 123 +- .../OrchardCore.Markdown/GraphQL/Startup.cs | 15 +- .../Handlers/MarkdownBodyPartHandler.cs | 121 +- .../Indexing/MarkdownBodyPartIndexHandler.cs | 21 +- .../Indexing/MarkdownFieldIndexHandler.cs | 21 +- .../OrchardCore.Markdown/Media/Startup.cs | 13 +- .../OrchardCore.Markdown/Migrations.cs | 103 +- .../Models/MarkdownBodyPart.cs | 9 +- .../MarkdownBodyMetaWeblogDriver.cs | 29 +- .../RemotePublishing/Startup.cs | 13 +- .../Services/DefaultMarkdownService.cs | 29 +- .../Services/MarkdownPipelineOptions.cs | 9 +- .../MarkdownPipelineOptionsExtensions.cs | 21 +- .../Settings/MarkdownBodyPartSettings.cs | 11 +- .../MarkdownBodyPartSettingsDisplayDriver.cs | 35 +- .../Settings/MarkdownFieldSettings.cs | 13 +- .../Settings/MarkdownFieldSettingsDriver.cs | 39 +- .../OrchardCore.Markdown/Startup.cs | 71 +- .../ViewModels/EditMarkdownFieldViewModel.cs | 15 +- .../MarkdownBodyPartSettingsViewModel.cs | 9 +- .../ViewModels/MarkdownBodyPartViewModel.cs | 23 +- .../MarkdownFieldSettingsViewModel.cs | 11 +- .../ViewModels/MarkdownFieldViewModel.cs | 17 +- .../OrchardCore.Media.AmazonS3/AdminMenu.cs | 43 +- .../Controllers/AdminController.cs | 65 +- .../ViewModels/OptionsViewModel.cs | 13 +- .../OrchardCore.Media.Azure/AdminMenu.cs | 43 +- .../Controllers/AdminController.cs | 51 +- .../OrchardCore.Media.Azure/Startup.cs | 285 ++- .../ViewModels/OptionsViewModel.cs | 15 +- .../Services/PdfMediaFileTextProvider.cs | 61 +- .../OrchardCore.Media/AdminMenu.cs | 113 +- .../Controllers/AdminController.cs | 867 +++++---- .../Controllers/MediaCacheController.cs | 107 +- .../Controllers/MediaProfilesController.cs | 539 +++-- .../AllMediaProfilesDeploymentSource.cs | 37 +- .../AllMediaProfilesDeploymentStep.cs | 17 +- .../AllMediaProfilesDeploymentStepDriver.cs | 27 +- .../Deployment/MediaDeploymentSource.cs | 81 +- .../Deployment/MediaDeploymentStep.cs | 25 +- .../Deployment/MediaDeploymentStepDriver.cs | 111 +- .../Drivers/MediaFieldDisplayDriver.cs | 219 ++- .../OrchardCore.Media/Fields/Anchor.cs | 19 +- .../Fields/MediaFieldAnchorExtensions.cs | 35 +- .../MediaFieldAttachedFileNameExtensions.cs | 39 +- .../Filters/AssetUrlFilter.cs | 27 +- .../OrchardCore.Media/Filters/MediaFilters.cs | 25 +- .../Filters/ResizeUrlFilter.cs | 177 +- .../GraphQL/MediaAssetObjectType.cs | 37 +- .../GraphQL/MediaAssetQuery.cs | 105 +- .../GraphQL/MediaFieldQueryObjectType.cs | 169 +- .../OrchardCore.Media/GraphQL/Startup.cs | 17 +- .../AttachedMediaFieldContentHandler.cs | 31 +- .../Indexing/MediaFieldIndexHandler.cs | 127 +- .../Liquid/MediaAnchorTag.cs | 119 +- .../OrchardCore.Media/Migrations.cs | 49 +- .../Models/MediaProfilesDocument.cs | 29 +- .../Processing/BackwardsCompatibleCacheKey.cs | 41 +- .../Processing/ImageSharpUrlFormatter.cs | 119 +- .../Processing/ImageVersionProcessor.cs | 29 +- .../MediaImageSharpConfiguration.cs | 189 +- .../Processing/MediaResizingFileProvider.cs | 101 +- .../Processing/MediaTokenOptions.cs | 9 +- .../MediaTokenOptionsConfiguration.cs | 29 +- .../Processing/MediaTokenService.cs | 315 ++- .../Processing/MediaTokenSettings.cs | 9 +- .../Processing/MediaTokenSettingsUpdater.cs | 89 +- .../Processing/TokenCommandProcessor.cs | 31 +- .../Recipes/MediaProfileStep.cs | 47 +- .../OrchardCore.Media/Recipes/MediaStep.cs | 201 +- .../RemotePublishing/MediaMetaWeblogDriver.cs | 11 +- .../RemotePublishingStartup.cs | 13 +- .../ResourceManifestOptionsConfiguration.cs | 41 +- .../SecureMediaPermissions.cs | 243 ++- .../Services/AttachedMediaFieldFileService.cs | 225 ++- .../DefaultUserAssetFolderNameProvider.cs | 11 +- .../Services/IMediaProfileService.cs | 9 +- .../ManageMediaFolderAuthorizationHandler.cs | 167 +- .../Services/MediaFileProvider.cs | 23 +- .../MediaFileStoreResolverMiddleware.cs | 163 +- .../Services/MediaOptionsConfiguration.cs | 227 ++- .../Services/MediaProfileService.cs | 89 +- .../Services/MediaProfilesManager.cs | 49 +- .../NullMediaNameNormalizerService.cs | 11 +- .../Services/SecureMediaExtensions.cs | 27 +- .../Services/SecureMediaMarker.cs | 7 +- .../Services/SecureMediaMiddleware.cs | 65 +- .../SlugifyMediaNameNormalizerService.cs | 31 +- .../Services/TempDirCleanerService.cs | 75 +- .../ViewMediaFolderAuthorizationHandler.cs | 335 ++-- .../Settings/MediaFieldSettingsDriver.cs | 145 +- .../Shortcodes/AssetUrlShortcodeProvider.cs | 139 +- .../Shortcodes/ImageShortcodeProvider.cs | 207 +- .../OrchardCore.Media/Startup.cs | 421 ++-- .../TagHelpers/AnchorTagHelper.cs | 99 +- .../TagHelpers/ImageResizeTagHelper.cs | 137 +- .../TagHelpers/ImageTagHelper.cs | 91 +- .../ViewModels/DisplayMediaFieldViewModel.cs | 15 +- .../ViewModels/EditMediaFieldViewModel.cs | 61 +- .../ViewModels/MediaCacheViewModel.cs | 9 +- .../MediaDeploymentStepViewModel.cs | 17 +- .../ViewModels/MediaProfileIndexViewModel.cs | 65 +- .../ViewModels/MediaProfileViewModel.cs | 43 +- .../ViewModels/MediaStoreEntryViewModel.cs | 15 +- .../OrchardCore.Menu/AdminMenu.cs | 57 +- .../Controllers/AdminController.cs | 433 +++-- .../ContentMenuItemPartDisplayDriver.cs | 61 +- .../Drivers/HtmlMenuItemPartDisplayDriver.cs | 163 +- .../Drivers/LinkMenuItemPartDisplayDriver.cs | 149 +- .../Drivers/MenuPartDisplayDriver.cs | 197 +- .../GraphQL/HtmlMenuItemQueryObjectType.cs | 17 +- .../GraphQL/LinkMenuItemQueryObjectType.cs | 13 +- .../GraphQL/MenuItemContentTypeBuilder.cs | 29 +- .../GraphQL/MenuItemInterface.cs | 13 +- .../GraphQL/MenuItemsListQueryObjectType.cs | 17 +- .../OrchardCore.Menu/GraphQL/Startup.cs | 19 +- .../Handlers/MenuContentHandler.cs | 21 +- .../OrchardCore.Menu/MenuShapes.cs | 305 ++- .../OrchardCore.Menu/Migrations.cs | 103 +- .../Models/ContentMenuItemPart.cs | 7 +- .../Models/HtmlMenuItemPart.cs | 31 +- .../Models/LinkMenuItemPart.cs | 23 +- .../Models/MenuItemsListPart.cs | 13 +- .../OrchardCore.Menu/Models/MenuPart.cs | 9 +- .../Settings/HtmlMenuItemPartSettings.cs | 17 +- .../HtmlMenuItemPartSettingsDisplayDriver.cs | 35 +- .../OrchardCore.Menu/Startup.cs | 51 +- .../TagHelper/MenuTagHelper.cs | 15 +- .../ContentMenuItemPartEditViewModel.cs | 13 +- .../HtmlMenuItemPartEditViewModel.cs | 19 +- .../HtmlMenuItemPartSettingsViewModel.cs | 9 +- .../LinkMenuItemPartEditViewModel.cs | 17 +- .../ViewModels/MenuPartEditViewModel.cs | 17 +- .../AdminMenuMicrosoftAccount.cs | 107 +- .../AzureADOptionsConfiguration.cs | 129 +- .../CookieOptionsConfiguration.cs | 45 +- .../MicrosoftAccountOptionsConfiguration.cs | 115 +- .../OpenIdConnectOptionsConfiguration.cs | 57 +- .../Deployment/AzureADDeploymentSource.cs | 33 +- .../Deployment/AzureADDeploymentStep.cs | 17 +- .../Deployment/AzureADDeploymentStepDriver.cs | 27 +- .../Drivers/AzureADSettingsDisplayDriver.cs | 97 +- .../MicrosoftAccountSettingsDisplayDriver.cs | 139 +- .../OrchardCoreBuilderExtensions.cs | 35 +- .../MIcrosoftAuthenticationConstants.cs | 13 +- .../MicrosoftAccountStartup.cs | 97 +- .../Recipes/AzureADSettingsStep.cs | 59 +- .../Recipes/MicrosoftAccountSettingsStep.cs | 55 +- .../Services/AzureADService.cs | 89 +- .../Services/IAzureADService.cs | 15 +- .../Services/IMicrosoftAccountService.cs | 15 +- .../Services/MicrosoftAccountService.cs | 79 +- .../Settings/AzureADSettings.cs | 17 +- .../Settings/MicrosoftAccountSettings.cs | 15 +- .../ViewModels/AzureADSettingsViewModel.cs | 25 +- .../ViewModels/ErrorViewModel.cs | 11 +- .../MicrosoftAccountSettingsViewModel.cs | 23 +- .../CurrentDbProfiler.cs | 29 +- .../MiniProfilerConnectionFactory.cs | 31 +- .../MiniProfilerFilter.cs | 65 +- .../OrchardCore.MiniProfiler/ShapeStep.cs | 51 +- .../OrchardCore.MiniProfiler/Startup.cs | 41 +- .../Controllers/HomeController.cs | 11 +- .../OrchardCore.Mvc.HelloWorld/Startup.cs | 39 +- .../NavigationShapes.cs | 151 +- .../PagerShapesTableProvider.cs | 889 +++++---- .../OrchardCore.Navigation/Startup.cs | 33 +- .../OrchardCore.OpenId/AdminMenu.cs | 133 +- .../OpenIdClientConfiguration.cs | 181 +- .../OpenIdServerConfiguration.cs | 365 ++-- .../OpenIdValidationConfiguration.cs | 411 ++-- .../Controllers/AccessController.cs | 1041 +++++----- .../Controllers/ApplicationController.cs | 619 +++--- .../Controllers/ScopeController.cs | 395 ++-- .../ServerConfigurationController.cs | 133 +- .../Controllers/UserInfoController.cs | 225 ++- .../ValidationConfigurationController.cs | 133 +- .../OpenIdServerDeploymentSource.cs | 107 +- .../Deployment/OpenIdServerDeploymentStep.cs | 17 +- .../OpenIdServerDeploymentStepDriver.cs | 27 +- .../OpenIdValidationDeploymentSource.cs | 41 +- .../OpenIdValidationDeploymentStep.cs | 17 +- .../OpenIdValidationDeploymentStepDriver.cs | 27 +- .../OpenIdClientSettingsDisplayDriver.cs | 315 ++- .../OpenIdServerSettingsDisplayDriver.cs | 201 +- .../OpenIdValidationSettingsDisplayDriver.cs | 83 +- ...penIdApplicationRoleRemovedEventHandler.cs | 23 +- .../OpenIdApplicationSettings.cs | 401 ++-- .../OrchardCore.OpenId/OpenIdExtensions.cs | 75 +- .../Recipes/OpenIdApplicationStep.cs | 81 +- .../Recipes/OpenIdApplicationStepModel.cs | 59 +- .../Recipes/OpenIdClientSettingsStep.cs | 93 +- .../Recipes/OpenIdScopeStep.cs | 87 +- .../Recipes/OpenIdScopeStepModel.cs | 15 +- .../Recipes/OpenIdServerSettingsStep.cs | 97 +- .../Recipes/OpenIdServerSettingsStepModel.cs | 57 +- .../Recipes/OpenIdValidationSettingsStep.cs | 47 +- .../OpenIdValidationSettingsStepModel.cs | 17 +- .../Services/IOpenIdClientService.cs | 15 +- .../Services/IOpenIdServerService.cs | 23 +- .../Services/IOpenIdValidationService.cs | 15 +- .../Services/OpenIdClientService.cs | 153 +- .../Services/OpenIdServerService.cs | 919 +++++---- .../Services/OpenIdValidationService.cs | 309 ++- .../Settings/OpenIdClientSettings.cs | 41 +- .../Settings/OpenIdServerSettings.cs | 63 +- .../Settings/OpenIdValidationSettings.cs | 17 +- .../OrchardCore.OpenId/Startup.cs | 377 ++-- .../Tasks/OpenIdBackgroundTask.cs | 135 +- .../ViewModels/AuthorizeViewModel.cs | 13 +- .../CreateOpenIdApplicationViewModel.cs | 71 +- .../ViewModels/CreateOpenIdScopeViewModel.cs | 31 +- .../EditOpenIdApplicationViewModel.cs | 75 +- .../ViewModels/EditOpenIdScopeViewModel.cs | 35 +- .../ViewModels/ErrorViewModel.cs | 11 +- .../ViewModels/LogoutViewModel.cs | 9 +- .../OpenIdApplicationsIndexViewModel.cs | 27 +- .../OpenIdClientSettingsViewModel.cs | 55 +- .../ViewModels/OpenIdScopeIndexViewModel.cs | 27 +- .../ViewModels/OpenIdScopeStepViewModel.cs | 15 +- .../OpenIdServerSettingsViewModel.cs | 83 +- .../OpenIdValidationSettingsViewModel.cs | 23 +- .../OrchardCore.Placements/AdminMenu.cs | 43 +- .../Controllers/AdminController.cs | 487 +++-- .../Deployment/PlacementsDeploymentSource.cs | 55 +- .../Deployment/PlacementsDeploymentStep.cs | 17 +- .../PlacementsDeploymentStepDriver.cs | 27 +- .../Models/PlacementsDocument.cs | 9 +- .../Recipes/PlacementStep.cs | 41 +- .../Services/DatabasePlacementsStore.cs | 29 +- .../Services/FilePlacementsStore.cs | 29 +- .../Services/IPlacementStore.cs | 31 +- .../Services/PlacementProvider.cs | 149 +- .../Services/PlacementsManager.cs | 71 +- .../PlacementContentPartDefinitionDriver.cs | 87 +- ...ContentPartFieldDefinitionDisplayDriver.cs | 93 +- ...lacementContentTypePartDefinitionDriver.cs | 101 +- .../OrchardCore.Placements/Startup.cs | 57 +- .../ViewModels/ContentSettingsViewModel.cs | 27 +- .../ViewModels/EditShapePlacementViewModel.cs | 13 +- .../ListShapePlacementsViewModel.cs | 41 +- .../ViewModels/ShapePlacementViewModel.cs | 9 +- .../Drivers/PublishLaterPartDisplayDriver.cs | 101 +- .../OrchardCore.PublishLater/Startup.cs | 31 +- .../ViewModels/PublishLaterPartViewModel.cs | 15 +- .../OrchardCore.Queries/AdminMenu.cs | 53 +- .../Controllers/AdminController.cs | 401 ++-- .../Controllers/QueryApiController.cs | 87 +- .../Deployment/AllQueriesDeploymentSource.cs | 49 +- .../Deployment/AllQueriesDeploymentStep.cs | 17 +- .../AllQueriesDeploymentStepDriver.cs | 27 +- .../QueryBasedContentDeploymentSource.cs | 125 +- .../QueryBasedContentDeploymentStep.cs | 27 +- .../QueryBasedContentDeploymentStepDriver.cs | 111 +- .../Drivers/QueryDisplayDriver.cs | 145 +- .../Liquid/LiquidQueriesAccessor.cs | 13 +- .../OrchardCore.Queries/Liquid/QueryFilter.cs | 45 +- .../QueryGlobalMethodProvider.cs | 55 +- .../OrchardCore.Queries/Recipes/QueryStep.cs | 119 +- .../OrchardCore.Queries/Sql/AdminMenu.cs | 45 +- .../Sql/Controllers/AdminController.cs | 143 +- .../Sql/Drivers/SqlQueryDisplayDriver.cs | 113 +- .../Sql/GraphQL/SqlQueryFieldTypeProvider.cs | 315 ++- .../Sql/GraphQL/Startup.cs | 21 +- .../OrchardCore.Queries/Sql/SqlGrammar.cs | 429 ++-- .../OrchardCore.Queries/Sql/SqlParser.cs | 1309 +++++++------ .../Sql/SqlParserException.cs | 9 +- .../OrchardCore.Queries/Sql/SqlQuerySource.cs | 151 +- .../OrchardCore.Queries/Sql/Startup.cs | 29 +- .../Sql/ViewModels/AdminQueryViewModel.cs | 29 +- .../Sql/ViewModels/SqlQueryViewModel.cs | 11 +- .../OrchardCore.Queries/Startup.cs | 73 +- .../ViewModels/EditQueryViewModel.cs | 19 +- .../ViewModels/QueriesIndexViewModel.cs | 67 +- ...ueryBasedContentDeploymentStepViewModel.cs | 17 +- .../OrchardCore.ReCaptcha/AdminMenu.cs | 55 +- .../Drivers/ReCaptchaSettingsDisplayDriver.cs | 89 +- .../OrchardCore.ReCaptcha/Forms/Migrations.cs | 33 +- .../Forms/ReCaptchaPart.cs | 7 +- .../Forms/ReCaptchaPartDisplayDriver.cs | 43 +- .../Forms/ReCaptchaPartViewModel.cs | 9 +- .../OrchardCore.ReCaptcha/Forms/Startup.cs | 23 +- .../OrchardCore.ReCaptcha/Startup.cs | 81 +- .../Handlers/LoginFormEventEventHandler.cs | 57 +- .../PasswordRecoveryFormEventEventHandler.cs | 47 +- .../Handlers/RegistrationFormEventHandler.cs | 31 +- .../ViewModels/ReCaptchaSettingsViewModel.cs | 11 +- .../Workflows/Startup.cs | 13 +- .../Workflows/ValidateReCaptchaTask.cs | 67 +- .../ValidateReCaptchaTaskDisplayDriver.cs | 7 +- .../OrchardCore.Recipes/AdminMenu.cs | 47 +- .../Commands/RecipesCommands.cs | 63 +- .../Controllers/AdminController.cs | 213 +- .../RecipeSteps/CommandStep.cs | 79 +- .../RecipeSteps/RecipesStep.cs | 83 +- .../Services/RecipeDeploymentTargetHandler.cs | 65 +- .../OrchardCore.Recipes/Startup.cs | 35 +- .../ViewModels/RecipeViewModel.cs | 23 +- .../Options/RedisCacheOptionsSetup.cs | 41 +- .../Options/RedisKeyManagementOptionsSetup.cs | 41 +- .../OrchardCore.Redis/Services/RedisBus.cs | 107 +- .../OrchardCore.Redis/Services/RedisLock.cs | 297 ++- .../Services/RedisService.cs | 31 +- .../Services/RedisTagCache.cs | 151 +- .../OrchardCore.Redis/Startup.cs | 141 +- .../OrchardCore.Resources/Liquid/LinkTag.cs | 113 +- .../OrchardCore.Resources/Liquid/MetaTag.cs | 65 +- .../Liquid/ResourcesTag.cs | 47 +- .../Liquid/ScriptBlock.cs | 231 ++- .../OrchardCore.Resources/Liquid/ScriptTag.cs | 419 ++-- .../Liquid/StyleBlock.cs | 237 ++- .../OrchardCore.Resources/Liquid/StyleTag.cs | 383 ++-- .../ResourceManagementOptionsConfiguration.cs | 1055 +++++----- .../OrchardCore.Resources/Startup.cs | 47 +- .../Startup.cs | 23 +- .../OrchardCore.ReverseProxy/AdminMenu.cs | 59 +- .../ReverseProxySettingsDisplayDriver.cs | 115 +- .../OrchardCoreBuilderExtensions.cs | 19 +- .../ForwardedHeadersOptionsConfiguration.cs | 31 +- .../Services/ReverseProxyService.cs | 21 +- .../Settings/ReverseProxySettings.cs | 9 +- .../OrchardCore.ReverseProxy/Startup.cs | 47 +- .../ReverseProxySettingsViewModel.cs | 13 +- .../OrchardCore.Roles/AdminMenu.cs | 45 +- .../Controllers/AdminController.cs | 433 +++-- .../Deployment/AllRolesDeploymentSource.cs | 79 +- .../Deployment/AllRolesDeploymentStep.cs | 17 +- .../AllRolesDeploymentStepDriver.cs | 27 +- .../OrchardCore.Roles/Models/RolesDocument.cs | 11 +- .../OrchardCore.Roles/Recipes/RolesStep.cs | 103 +- .../OrchardCore.Roles/Services/RoleStore.cs | 299 ++- .../OrchardCore.Roles/Services/RoleUpdater.cs | 325 ++-- .../Services/RolesPermissionsHandler.cs | 87 +- .../OrchardCore.Roles/Startup.cs | 57 +- .../SelectRolesViewComponent.cs | 83 +- .../ViewModels/CreateRoleViewModel.cs | 13 +- .../ViewModels/EditRoleViewModel.cs | 23 +- .../ViewModels/RolesViewModel.cs | 21 +- .../Drivers/AllConditionDisplayDriver.cs | 53 +- .../Drivers/AnyConditionDisplayDriver.cs | 53 +- .../Drivers/BooleanConditionDisplayDriver.cs | 43 +- .../ContentTypeConditionDisplayDriver.cs | 73 +- .../ContentTypeConditionEvaluatorDriver.cs | 75 +- .../Drivers/CultureConditionDisplayDriver.cs | 75 +- .../Drivers/HomepageConditionDisplayDriver.cs | 43 +- .../IsAnonymousConditionDisplayDriver.cs | 27 +- .../IsAuthenticatedConditionDisplayDriver.cs | 27 +- .../JavascriptConditionDisplayDriver.cs | 137 +- .../Drivers/RoleConditionDisplayDriver.cs | 75 +- .../Drivers/RuleDisplayDriver.cs | 25 +- .../Drivers/UrlConditionDisplayDriver.cs | 73 +- .../Models/AllConditionGroup.cs | 7 +- .../Models/AnyConditionGroup.cs | 7 +- .../Models/BooleanCondition.cs | 9 +- .../Models/ContentTypeCondition.cs | 11 +- .../Models/CultureCondition.cs | 11 +- .../Models/HomepageCondition.cs | 9 +- .../Models/IsAnonymousCondition.cs | 7 +- .../Models/IsAuthenticatedCondition.cs | 7 +- .../Models/JavascriptCondition.cs | 9 +- .../OrchardCore.Rules/Models/RoleCondition.cs | 11 +- .../Models/StringOperators.cs | 57 +- .../OrchardCore.Rules/Models/UrlCondition.cs | 11 +- .../Services/AllConditionEvaluator.cs | 43 +- .../Services/AnyConditionEvaluator.cs | 33 +- .../Services/BooleanConditionEvaluator.cs | 11 +- .../Services/ConditionIdGenerator.cs | 23 +- .../ConditionOperatorConfigureOptions.cs | 111 +- .../Services/ConditionOperatorResolver.cs | 27 +- .../Services/ConditionResolver.cs | 33 +- .../Services/CultureConditionEvaluator.cs | 31 +- .../Services/HomepageConditionEvaluator.cs | 35 +- .../Services/IsAnonymousConditionEvaluator.cs | 21 +- .../IsAuthenticatedConditionEvaluator.cs | 21 +- .../Services/JavascriptConditionEvaluator.cs | 37 +- .../Services/RoleConditionEvaluator.cs | 59 +- .../OrchardCore.Rules/Services/RuleService.cs | 43 +- .../Services/StringOperatorComparer.cs | 113 +- .../Services/UrlConditionEvaluator.cs | 47 +- .../OrchardCore.Rules/Startup.cs | 93 +- .../SelectStringOperationViewComponent.cs | 53 +- .../ViewModels/AllConditionViewModel.cs | 13 +- .../ViewModels/AnyConditionViewModel.cs | 13 +- .../ViewModels/BooleanConditionViewModel.cs | 13 +- .../ViewModels/ConditionGroupViewModel.cs | 23 +- .../ContentTypeConditionViewModel.cs | 15 +- .../ViewModels/CultureConditionViewModel.cs | 15 +- .../ViewModels/HomepageConditionViewModel.cs | 13 +- .../JavascriptConditionViewModel.cs | 13 +- .../ViewModels/RoleConditionViewModel.cs | 15 +- .../SelectStringOperationViewModel.cs | 11 +- .../ViewModels/UrlConditionViewModel.cs | 15 +- .../Providers/LogProvider.cs | 75 +- .../OrchardCore.Scripting/Startup.cs | 13 +- .../Controllers/AdminController.cs | 955 +++++---- .../Controllers/ElasticsearchApiController.cs | 135 +- ...tentPartFieldIndexSettingsDisplayDriver.cs | 59 +- ...tPickerFieldElasticEditorSettingsDriver.cs | 57 +- ...ntentTypePartIndexSettingsDisplayDriver.cs | 59 +- .../ElasticIndexDeploymentStepDriver.cs | 73 +- ...ElasticIndexRebuildDeploymentStepDriver.cs | 65 +- .../ElasticIndexResetDeploymentStepDriver.cs | 65 +- .../Drivers/ElasticQueryDisplayDriver.cs | 131 +- .../ElasticSettingsDeploymentStepDriver.cs | 27 +- .../GraphQL/ElasticQueryFieldTypeProvider.cs | 301 ++- .../GraphQL/Startup.cs | 19 +- .../Startup.cs | 175 +- .../ViewModels/AdminIndexViewModel.cs | 47 +- .../ViewModels/AdminQueryViewModel.cs | 33 +- .../ViewModels/ElasticApiQueryViewModel.cs | 13 +- .../ElasticContentIndexSettingsViewModel.cs | 9 +- .../ElasticIndexDeploymentStepViewModel.cs | 13 +- ...sticIndexRebuildDeploymentStepViewModel.cs | 13 +- ...lasticIndexResetDeploymentStepViewModel.cs | 13 +- .../ElasticIndexSettingsViewModel.cs | 33 +- .../ViewModels/ElasticQueryViewModel.cs | 15 +- .../ViewModels/ElasticSettingsViewModel.cs | 23 +- .../ViewModels/IndexViewModel.cs | 13 +- .../ViewModels/MappingsViewModel.cs | 11 +- .../ViewModels/QueryIndexViewModel.cs | 19 +- .../Controllers/AdminController.cs | 753 ++++--- .../Controllers/LuceneApiController.cs | 137 +- .../Deployment/LuceneIndexDeploymentSource.cs | 59 +- .../Deployment/LuceneIndexDeploymentStep.cs | 23 +- .../LuceneIndexDeploymentStepDriver.cs | 73 +- .../LuceneIndexRebuildDeploymentSource.cs | 35 +- .../LuceneIndexRebuildDeploymentStep.cs | 23 +- .../LuceneIndexRebuildDeploymentStepDriver.cs | 65 +- .../LuceneIndexResetDeploymentSource.cs | 35 +- .../LuceneIndexResetDeploymentStep.cs | 23 +- .../LuceneIndexResetDeploymentStepDriver.cs | 65 +- .../LuceneSettingsDeploymentSource.cs | 39 +- .../LuceneSettingsDeploymentStep.cs | 17 +- .../LuceneSettingsDeploymentStepDriver.cs | 27 +- .../Drivers/LuceneQueryDisplayDriver.cs | 131 +- .../Drivers/LuceneSettingsDisplayDriver.cs | 91 +- .../GraphQL/LuceneQueryFieldTypeProvider.cs | 307 ++- .../GraphQL/Startup.cs | 19 +- .../Handler/LuceneIndexingContentHandler.cs | 161 +- .../OrchardCore.Search.Lucene/Migrations.cs | 283 ++- .../Model/LuceneContentIndexSettings.cs | 41 +- .../Model/LuceneIndexSettings.cs | 29 +- .../Model/LuceneQueryModel.cs | 13 +- .../Model/LuceneSettings.cs | 19 +- .../Recipes/LuceneIndexRebuildStep.cs | 65 +- .../Recipes/LuceneIndexResetStep.cs | 77 +- .../Recipes/LuceneIndexStep.cs | 67 +- .../Services/IndexingBackgroundTask.cs | 33 +- .../Services/LuceneAnalyzer.cs | 33 +- .../Services/LuceneAnalyzerManager.cs | 49 +- .../LuceneContentPickerResultProvider.cs | 89 +- .../Services/LuceneIndexInitializerService.cs | 51 +- .../Services/LuceneIndexManager.cs | 811 ++++---- .../Services/LuceneIndexSettingsService.cs | 125 +- .../Services/LuceneIndexingService.cs | 445 +++-- .../Services/LuceneIndexingState.cs | 89 +- .../Services/LuceneQuerySource.cs | 141 +- .../Services/LuceneSearchQueryService.cs | 53 +- ...tentPartFieldIndexSettingsDisplayDriver.cs | 59 +- .../ContentPickerFieldLuceneEditorSettings.cs | 13 +- ...ntPickerFieldLuceneEditorSettingsDriver.cs | 57 +- ...ntentTypePartIndexSettingsDisplayDriver.cs | 59 +- .../LuceneContentPickerShapeProvider.cs | 31 +- .../OrchardCore.Search.Lucene/Startup.cs | 131 +- .../ViewModels/AdminIndexViewModel.cs | 47 +- .../ViewModels/AdminQueryViewModel.cs | 29 +- .../ViewModels/IndexViewModel.cs | 13 +- .../LuceneContentIndexSettingsViewModel.cs | 9 +- .../LuceneIndexDeploymentStepViewModel.cs | 13 +- ...ceneIndexRebuildDeploymentStepViewModel.cs | 13 +- ...LuceneIndexResetDeploymentStepViewModel.cs | 13 +- .../LuceneIndexSettingsViewModel.cs | 33 +- .../ViewModels/LuceneQueryViewModel.cs | 15 +- .../ViewModels/LuceneSettingsViewModel.cs | 17 +- .../ViewModels/QueryIndexViewModel.cs | 19 +- .../OrchardCore.Search/AdminMenu.cs | 61 +- .../SearchSettingsDeploymentSource.cs | 37 +- .../SearchSettingsDeploymentStep.cs | 17 +- .../SearchSettingsDeploymentStepDriver.cs | 27 +- .../Drivers/SearchSettingsDisplayDriver.cs | 97 +- .../Model/SearchSettings.cs | 13 +- .../Services/SearchSettingsConfiguration.cs | 29 +- .../OrchardCore.Search/Startup.cs | 79 +- .../ViewModel/SearchSettingsViewModel.cs | 17 +- .../OrchardCore.Security/AdminMenu.cs | 59 +- .../Drivers/SecuritySettingsDisplayDriver.cs | 159 +- .../OrchardCoreBuilderExtensions.cs | 29 +- ...rityHeadersApplicationBuilderExtensions.cs | 43 +- .../ContentSecurityPolicyOriginValue.cs | 13 +- .../Options/ContentSecurityPolicyValue.cs | 41 +- .../Options/ContentTypeOptionsValue.cs | 9 +- .../Options/PermissionsPolicyOriginValue.cs | 13 +- .../Options/PermissionsPolicyValue.cs | 67 +- .../Options/ReferrerPolicyValue.cs | 23 +- .../Options/SecurityHeadersOptions.cs | 101 +- .../SecurityHeaderDefaults.cs | 117 +- ...ntentSecurityPolicyHeaderPolicyProvider.cs | 27 +- .../ContentTypeOptionsHeaderPolicyProvider.cs | 11 +- .../Services/HeaderPolicyProvider.cs | 19 +- .../Services/IHeaderPolicyProvider.cs | 11 +- .../Services/ISecurityService.cs | 9 +- .../PermissionsHeaderPolicyProvider.cs | 27 +- .../Services/ReferrerHeaderPolicyProvider.cs | 11 +- .../Services/SecurityHeadersMiddleware.cs | 37 +- .../Services/SecurityService.cs | 21 +- .../Services/SecuritySettingsConfiguration.cs | 29 +- .../Settings/SecuritySettings.cs | 79 +- .../OrchardCore.Security/Startup.cs | 47 +- .../ViewModels/SecuritySettingsViewModel.cs | 65 +- .../Drivers/SeoContentDriver.cs | 441 +++-- .../Drivers/SeoMetaPartDisplayDriver.cs | 197 +- .../Handlers/SeoMetaPartHandler.cs | 345 ++-- .../Handlers/SeoMetaSettingsHandler.cs | 301 ++- .../OrchardCore.Seo/Migrations.cs | 81 +- .../OrchardCore.Seo/Models/SeoAspect.cs | 75 +- .../OrchardCore.Seo/Models/SeoMetaPart.cs | 49 +- .../Models/SeoMetaPartSettings.cs | 17 +- .../SeoMetaPartSettingsDisplayDriver.cs | 63 +- .../OrchardCore.Seo/Startup.cs | 43 +- .../SeoMetaPartGoogleSchemaViewModel.cs | 13 +- .../SeoMetaPartOpenGraphViewModel.cs | 17 +- .../SeoMetaPartSettingsViewModel.cs | 19 +- .../ViewModels/SeoMetaPartTwitterViewModel.cs | 21 +- .../ViewModels/SeoMetaPartViewModel.cs | 29 +- .../OrchardCore.Settings/AdminMenu.cs | 65 +- .../Controllers/AdminController.cs | 153 +- .../SiteSettingsDeploymentSource.cs | 169 +- .../Deployment/SiteSettingsDeploymentStep.cs | 21 +- .../SiteSettingsDeploymentStepDriver.cs | 45 +- .../Drivers/ButtonsSettingsDisplayDriver.cs | 19 +- .../DefaultSiteSettingsDisplayDriver.cs | 165 +- .../Liquid/LiquidSiteSettingsAccessor.cs | 13 +- .../Recipes/SettingsStep.cs | 175 +- .../Services/DefaultTimeZoneSelector.cs | 39 +- .../RecipeEnvironmentSiteNameProvider.cs | 29 +- .../Services/SetupEventHandler.cs | 41 +- .../Services/SiteService.cs | 99 +- .../Services/SuperUserHandler.cs | 51 +- .../OrchardCore.Settings/SiteSettings.cs | 79 +- .../OrchardCore.Settings/Startup.cs | 101 +- .../ViewModels/AdminIndexViewModel.cs | 11 +- .../ViewModels/SiteCulturesViewModel.cs | 13 +- .../SiteSettingsDeploymentStepViewModel.cs | 9 +- .../ViewModels/SiteSettingsViewModel.cs | 31 +- .../Controllers/SetupController.cs | 381 ++-- .../OrchardCore.Setup/Startup.cs | 133 +- .../ViewModels/SetupViewModel.cs | 65 +- .../OrchardCore.Shortcodes/AdminMenu.cs | 43 +- .../Controllers/AdminController.cs | 507 +++-- .../AllShortcodeTemplatesDeploymentSource.cs | 47 +- .../AllShortcodeTemplatesDeploymentStep.cs | 11 +- ...lShortcodeTemplatesDeploymentStepDriver.cs | 27 +- .../ShortcodeDescriptorDisplayDriver.cs | 17 +- .../Models/ShortcodeTemplatesDocument.cs | 25 +- .../Providers/LocaleShortcodeProvider.cs | 65 +- .../Recipes/ShortcodeTemplateStep.cs | 41 +- .../DefaultShortcodeContextProvider.cs | 29 +- .../Services/OptionsShortcodeProvider.cs | 35 +- .../Services/ShortcodeDescriptorManager.cs | 45 +- .../ShortcodeOptionsDescriptorProvider.cs | 37 +- .../Services/ShortcodeService.cs | 39 +- .../ShortcodeTemplatesDescriptorProvider.cs | 41 +- .../Services/ShortcodeTemplatesManager.cs | 49 +- .../Services/TemplateShortcodeProvider.cs | 101 +- .../OrchardCore.Shortcodes/Startup.cs | 115 +- .../ShortcodeTemplateIndexViewModel.cs | 65 +- .../ViewModels/ShortcodeTemplateViewModel.cs | 21 +- .../ViewModels/ShortcodeViewModel.cs | 13 +- .../OrchardCore.Sitemaps/AdminMenu.cs | 61 +- .../CustomPathSitemapSourceBuilder.cs | 99 +- ...omPathSitemapSourceModifiedDateProvider.cs | 11 +- .../Builders/DefaultSitemapBuilder.cs | 39 +- .../DefaultSitemapModifiedDateProvider.cs | 37 +- .../Builders/SitemapIndexTypeBuilder.cs | 99 +- .../Builders/SitemapTypeBuilder.cs | 37 +- .../Cache/DefaultSitemapCacheProvider.cs | 241 ++- .../Cache/PhysicalSitemapCacheFileResolver.cs | 23 +- .../Cache/SitemapCacheBackgroundTask.cs | 25 +- .../Controllers/AdminController.cs | 517 +++-- .../Controllers/SitemapCacheController.cs | 121 +- .../Controllers/SitemapController.cs | 167 +- .../Controllers/SitemapIndexController.cs | 531 +++-- .../Controllers/SourceController.cs | 327 ++-- .../Deployment/AllSitemapsDeploymentSource.cs | 47 +- .../Deployment/AllSitemapsDeploymentStep.cs | 11 +- .../AllSitemapsDeploymentStepDriver.cs | 27 +- .../Drivers/CustomPathSitemapSourceDriver.cs | 93 +- .../Drivers/SitemapPartDisplayDriver.cs | 71 +- .../CustomPathSitemapSourceUpdateHandler.cs | 49 +- .../Handlers/DefaultSitemapUpdateHandler.cs | 51 +- .../Handlers/SitemapIndexTypeUpdateHandler.cs | 93 +- .../Handlers/SitemapPartHandler.cs | 25 +- .../Handlers/SitemapTypeUpdateHandler.cs | 25 +- .../OrchardCore.Sitemaps/Migrations.cs | 29 +- .../Models/SitemapDocument.cs | 9 +- .../Models/SitemapIndex.cs | 7 +- .../Models/SitemapIndexSource.cs | 21 +- .../Models/SitemapPart.cs | 21 +- .../Recipes/SitemapsStep.cs | 57 +- .../Routing/SitemapEntries.cs | 91 +- .../Routing/SitemapRouteDocument.cs | 11 +- .../Routing/SitemapRouteTransformer.cs | 47 +- .../Routing/SitemapValuesAddressScheme.cs | 113 +- .../DefaultRouteableContentTypeCoordinator.cs | 49 +- .../Services/RazorPagesContentTypeProvider.cs | 61 +- .../Services/SitemapHelperService.cs | 115 +- .../Services/SitemapIdGenerator.cs | 23 +- .../Services/SitemapManager.cs | 117 +- .../OrchardCore.Sitemaps/Startup.cs | 181 +- .../ViewModels/CreateSitemapIndexViewModel.cs | 31 +- .../ViewModels/CreateSitemapViewModel.cs | 15 +- .../ViewModels/CreateSourceViewModel.cs | 19 +- .../CustomPathSitemapSourceViewModel.cs | 31 +- .../ViewModels/DisplaySitemapViewModel.cs | 13 +- .../ViewModels/EditSitemapIndexViewModel.cs | 15 +- .../ViewModels/EditSitemapViewModel.cs | 17 +- .../ViewModels/EditSourceViewModel.cs | 17 +- .../ViewModels/ListSitemapCacheViewModel.cs | 9 +- .../ViewModels/ListSitemapIndexViewModel.cs | 25 +- .../ViewModels/ListSitemapViewModel.cs | 64 +- .../ViewModels/SitemapPartViewModel.cs | 19 +- .../Drivers/GeoPointFieldDisplayDriver.cs | 129 +- .../Drivers/GeoPointFieldSettingsDriver.cs | 33 +- .../Fields/GeoPointField.cs | 11 +- .../Indexing/GeoPointFieldIndexHandler.cs | 41 +- .../ResourceManagementOptionsConfiguration.cs | 47 +- .../Settings/GeoPointFieldSettings.cs | 11 +- .../OrchardCore.Spatial/Startup.cs | 37 +- .../DisplayGeoPointFieldViewModel.cs | 13 +- .../ViewModels/EditGeoPointFieldViewModel.cs | 23 +- .../OrchardCore.Taxonomies/AdminMenu.cs | 59 +- .../Controllers/AdminController.cs | 555 +++--- .../Controllers/TagController.cs | 171 +- .../TaxonomyContentsAdminListDisplayDriver.cs | 243 ++- .../Drivers/TaxonomyFieldDisplayDriver.cs | 115 +- .../Drivers/TaxonomyFieldDriverHelper.cs | 53 +- .../Drivers/TaxonomyFieldTagsDisplayDriver.cs | 149 +- .../Drivers/TaxonomyPartDisplayDriver.cs | 155 +- .../Drivers/TermPartContentDriver.cs | 227 ++- .../Fields/TagNamesExtensions.cs | 39 +- .../Fields/TaxonomyField.cs | 11 +- .../GraphQL/TaxonomyFieldQueryObjectType.cs | 95 +- .../GraphQL/TaxonomyPartQueryObjectType.cs | 21 +- .../Handlers/TaxonomyPartHandler.cs | 21 +- .../Handlers/TermPartContentHandler.cs | 27 +- .../Indexing/TaxonomyFieldIndexHandler.cs | 63 +- .../Indexing/TaxonomyIndex.cs | 167 +- .../Liquid/InheritedTermsFilter.cs | 47 +- .../Liquid/TaxonomyTermsFilter.cs | 95 +- .../OrchardCore.Taxonomies/Migrations.cs | 323 ++- .../Models/TaxonomyPart.cs | 13 +- .../OrchardCore.Taxonomies/Models/TermPart.cs | 11 +- .../TaxonomyContentsAdminListFilter.cs | 63 +- .../TaxonomyContentsAdminListSettings.cs | 9 +- ...yContentsAdminListSettingsDisplayDriver.cs | 85 +- .../Settings/TaxonomyFieldSettings.cs | 49 +- .../Settings/TaxonomyFieldSettingsDriver.cs | 41 +- .../TaxonomyFieldTagsEditorSettings.cs | 21 +- .../TaxonomyFieldTagsEditorSettingsDriver.cs | 35 +- .../OrchardCore.Taxonomies/Startup.cs | 117 +- .../OrchardCore.Taxonomies/TermShapes.cs | 451 +++-- .../ViewModels/CreatedTagViewModel.cs | 11 +- .../DisplayTaxonomyFieldTagsViewModel.cs | 9 +- .../DisplayTaxonomyFieldViewModel.cs | 17 +- .../EditTagTaxonomyFieldViewModel.cs | 43 +- .../ViewModels/EditTaxonomyFieldViewModel.cs | 47 +- .../TaxonomyContentsAdminFilterViewModel.cs | 17 +- ...onomyContentsAdminListSettingsViewModel.cs | 21 +- .../ViewModels/TaxonomyPartEditViewModel.cs | 15 +- .../ViewModels/TaxonomyPartViewModel.cs | 17 +- .../ViewModels/TermPartViewModel.cs | 19 +- .../OrchardCore.Templates/AdminMenu.cs | 43 +- .../AdminTemplatesAdminMenu.cs | 43 +- .../Controllers/PreviewController.cs | 141 +- .../Controllers/TemplateController.cs | 597 +++--- .../AllAdminTemplatesDeploymentSource.cs | 65 +- .../AllAdminTemplatesDeploymentStep.cs | 19 +- .../AllAdminTemplatesDeploymentStepDriver.cs | 41 +- .../AllTemplatesDeploymentSource.cs | 69 +- .../Deployment/AllTemplatesDeploymentStep.cs | 19 +- .../AllTemplatesDeploymentStepDriver.cs | 41 +- .../Models/AdminTemplatesDocument.cs | 7 +- .../Models/TemplatesDocument.cs | 19 +- .../Recipes/AdminTemplateStep.cs | 41 +- .../Recipes/TemplateStep.cs | 43 +- .../ResourceManagementOptionsConfiguration.cs | 31 +- .../Services/AdminPreviewTemplatesProvider.cs | 57 +- .../Services/AdminTemplatesManager.cs | 49 +- .../AdminTemplatesShapeBindingResolver.cs | 101 +- .../Services/PreviewTemplatesProvider.cs | 57 +- .../Services/TemplatesManager.cs | 49 +- .../Services/TemplatesShapeBindingResolver.cs | 99 +- .../TemplateContentPartDefinitionDriver.cs | 49 +- .../TemplateContentTypeDefinitionDriver.cs | 55 +- ...TemplateContentTypePartDefinitionDriver.cs | 53 +- .../OrchardCore.Templates/Startup.cs | 57 +- ...llAdminTemplatesDeploymentStepViewModel.cs | 9 +- .../AllTemplatesDeploymentStepViewModel.cs | 9 +- .../ViewModels/ContentSettingsViewModel.cs | 19 +- .../ViewModels/TemplateIndexViewModel.cs | 67 +- .../ViewModels/TemplateViewModel.cs | 15 +- .../OrchardCore.Tenants/AdminMenu.cs | 61 +- .../Controllers/AdminController.cs | 1057 +++++----- .../Controllers/FeatureProfilesController.cs | 403 ++-- .../Controllers/TenantApiController.cs | 737 ++++--- .../AllFeatureProfilesDeploymentSource.cs | 47 +- .../AllFeatureProfilesDeploymentStep.cs | 11 +- .../AllFeatureProfilesDeploymentStepDriver.cs | 27 +- .../FeatureProfilesAdminMenu.cs | 59 +- .../Recipes/FeatureProfilesStep.cs | 41 +- .../Services/FeatureProfilesManager.cs | 49 +- .../Services/FeatureProfilesSchemaService.cs | 75 +- .../Services/FeatureProfilesService.cs | 25 +- .../Services/IFeatureProfilesSchemaService.cs | 9 +- .../Services/ITenantFileProvider.cs | 7 +- .../Services/ITenantValidator.cs | 9 +- .../Services/TenantFileProvider.cs | 15 +- .../Services/TenantValidator.cs | 237 ++- .../OrchardCore.Tenants/Startup.cs | 179 +- .../ViewModels/AdminIndexViewModel.cs | 125 +- .../ViewModels/CreateApiViewModel.cs | 7 +- .../ViewModels/EditTenantViewModel.cs | 15 +- .../ViewModels/FeatureProfileViewModel.cs | 19 +- .../FeatureProfilesIndexViewModel.cs | 55 +- .../ViewModels/SetupApiViewModel.cs | 53 +- .../ViewModels/TenantViewModel.cs | 7 +- .../Workflows/Activities/CreateTenantTask.cs | 269 ++- .../Workflows/Activities/DisableTenantTask.cs | 75 +- .../Workflows/Activities/EnableTenantTask.cs | 75 +- .../Workflows/Activities/SetupTenantTask.cs | 323 ++- .../Workflows/Activities/TenantActivity.cs | 69 +- .../Workflows/Activities/TenantTask.cs | 11 +- .../Drivers/CreateTenantTaskDisplayDriver.cs | 55 +- .../Drivers/DisableTenantTaskDisplayDriver.cs | 19 +- .../Drivers/EnableTenantTaskDisplayDriver.cs | 19 +- .../Drivers/SetupTenantTaskDisplayDriver.cs | 55 +- .../Drivers/TenantTaskDisplayDriver.cs | 23 +- .../OrchardCore.Tenants/Workflows/Startup.cs | 19 +- .../ViewModels/CreateTenantTaskViewModel.cs | 25 +- .../ViewModels/DisableTenantTaskViewModel.cs | 7 +- .../ViewModels/EnableTenantTaskViewModel.cs | 7 +- .../ViewModels/SetupTenantTaskViewModel.cs | 25 +- .../ViewModels/TenantTaskViewModel.cs | 21 +- .../OrchardCore.Themes/AdminMenu.cs | 45 +- .../ThemeCacheContextProvider.cs | 27 +- .../Controllers/AdminController.cs | 337 ++-- .../Deployment/ThemesDeploymentSource.cs | 43 +- .../Deployment/ThemesDeploymentStep.cs | 17 +- .../Deployment/ThemesDeploymentStepDriver.cs | 25 +- .../Models/SelectThemesViewModel.cs | 13 +- .../OrchardCore.Themes/Models/ThemeEntry.cs | 117 +- .../OrchardCore.Themes/Recipes/ThemesStep.cs | 67 +- .../Services/ISiteThemeService.cs | 13 +- .../Services/IThemeService.cs | 11 +- .../Services/SiteThemeSelector.cs | 45 +- .../Services/SiteThemeService.cs | 59 +- .../Services/ThemeService.cs | 215 +- .../OrchardCore.Themes/Startup.cs | 37 +- .../ViewModels/SelectThemesViewModel.cs | 13 +- .../Drivers/TitlePartDisplayDriver.cs | 83 +- .../Handlers/TitlePartHandler.cs | 141 +- .../Indexing/TitlePartIndexHandler.cs | 21 +- .../OrchardCore.Title/Migrations.cs | 145 +- .../OrchardCore.Title/Models/TitlePart.cs | 9 +- .../Models/TitlePartSettings.cs | 49 +- .../RemotePublishingStartup.cs | 13 +- .../RemotePublishing/TitleMetaWeblogDriver.cs | 37 +- .../TitlePartSettingsDisplayDriver.cs | 75 +- .../OrchardCore.Title/Startup.cs | 31 +- .../ViewModels/TitlePartSettingsViewModel.cs | 17 +- .../ViewModels/TitlePartViewModel.cs | 21 +- .../OrchardCore.Twitter/AdminMenuSignin.cs | 115 +- .../Drivers/TwitterSettingsDisplayDriver.cs | 159 +- .../OrchardCoreBuilderExtensions.cs | 29 +- .../Recipes/TwitterSettingsStep.cs | 59 +- .../Services/ITwitterSettingsService.cs | 15 +- .../Services/TwitterClient.cs | 77 +- .../Services/TwitterClientMessageHandler.cs | 157 +- .../Services/TwitterSettingsService.cs | 97 +- .../Settings/TwitterSettings.cs | 15 +- .../TwitterOptionsConfiguration.cs | 173 +- .../TwitterSigninSettingsDisplayDriver.cs | 85 +- .../Signin/Services/ITwitterSigninService.cs | 13 +- .../Signin/Services/TwitterSigninService.cs | 49 +- .../Signin/Settings/TwitterSigninSettings.cs | 11 +- .../TwitterSigninSettingsViewModel.cs | 13 +- .../OrchardCore.Twitter/Startup.cs | 91 +- .../OrchardCore.Twitter/TwitterConstants.cs | 13 +- .../ViewModels/TwitterSettingsViewModel.cs | 25 +- .../Activities/UpdateTwitterStatusTask.cs | 85 +- .../UpdateTwitterStatusTaskDisplayDriver.cs | 19 +- .../OrchardCore.Twitter/Workflows/Startup.cs | 13 +- .../UpdateTwitterStatusTaskViewModel.cs | 11 +- .../OrchardCore.Users/AdminMenu.cs | 235 ++- .../AuditTrailUserEventDisplayDriver.cs | 31 +- .../AuditTrail/Handlers/UserEventHandler.cs | 165 +- .../AuditTrail/Models/AuditTrailUserEvent.cs | 15 +- .../AuditTrail/Registration/Startup.cs | 15 +- ...egistrationAuditTrailEventConfiguration.cs | 19 +- .../UserRegistrationEventHandler.cs | 89 +- .../AuditTrail/ResetPassword/Startup.cs | 15 +- ...setPasswordAuditTrailEventConfiguration.cs | 23 +- .../UserResetPasswordEventHandler.cs | 97 +- .../UserAuditTrailEventConfiguration.cs | 49 +- .../OrchardCore.Users/AuditTrail/Startup.cs | 21 +- .../AuditTrailUserEventDetailViewModel.cs | 7 +- .../AuditTrailUserEventViewModel.cs | 21 +- .../CacheTicketStoreStartup.cs | 13 +- .../Commands/UserCommands.cs | 71 +- .../Controllers/AccountController.cs | 1391 +++++++------ .../Controllers/AdminController.cs | 841 ++++---- .../Controllers/ChangeEmailController.cs | 129 +- .../Controllers/RegistrationController.cs | 171 +- .../Controllers/ResetPasswordController.cs | 267 ++- .../CookieAuthenticationOptionsConfigure.cs | 49 +- .../CustomUserSettingsDeploymentStepDriver.cs | 59 +- .../ChangeEmailSettingsDisplayDriver.cs | 73 +- .../CustomUserSettingsDisplayDriver.cs | 153 +- .../Drivers/LoginSettingsDisplayDriver.cs | 75 +- .../RegistrationSettingsDisplayDriver.cs | 87 +- .../ResetPasswordSettingsDisplayDriver.cs | 75 +- .../Drivers/UserButtonsDisplayDriver.cs | 11 +- .../Drivers/UserDisplayDriver.cs | 213 +- .../Drivers/UserInformationDisplayDriver.cs | 201 +- .../Drivers/UserOptionsDisplayDriver.cs | 105 +- .../Drivers/UserRoleDisplayDriver.cs | 271 ++- .../Extensions/ControllerExtensions.cs | 163 +- .../ScriptExternalLoginEventHandler.cs | 143 +- .../Liquid/HasPermissionFilter.cs | 47 +- .../Liquid/IsInRoleFilter.cs | 45 +- .../Liquid/LiquidUserAccessor.cs | 13 +- .../Liquid/UserEmailFilter.cs | 57 +- .../OrchardCore.Users/Liquid/UserFilters.cs | 57 +- .../Liquid/UsersByIdFilter.cs | 69 +- .../Localization/UserLocalizationConstants.cs | 9 +- .../OrchardCore.Users/Migrations.cs | 489 +++-- .../Models/ChangeEmailSettings.cs | 9 +- .../Models/RegistrationSettings.cs | 25 +- .../Models/ResetPasswordSettings.cs | 11 +- .../Models/UserRegistrationType.cs | 13 +- ...ppProviderTwoFactorOptionsConfiguration.cs | 2 +- .../Services/DefaultUserIdGenerator.cs | 23 +- .../DefaultUsersAdminListFilterParser.cs | 21 +- .../DefaultUsersAdminListFilterProvider.cs | 231 ++- .../Services/EmailClaimsProvider.cs | 43 +- .../Services/MembershipService.cs | 57 +- .../RecipeEnvironmentSuperUserProvider.cs | 69 +- .../Services/SetupEventHandler.cs | 49 +- .../Services/UserRoleRemovedEventHandler.cs | 27 +- .../Services/UsersThemeSelector.cs | 135 +- .../OrchardCore.Users/Startup.cs | 847 ++++---- .../Drivers/UserTimeZoneDisplayDriver.cs | 45 +- .../TimeZone/Models/UserTimeZone.cs | 9 +- .../OrchardCore.Users/TimeZone/Startup.cs | 17 +- .../ViewModel/UserTimeZoneViewModel.cs | 9 +- .../UserOptionsConfiguration.cs | 39 +- .../ViewModels/ChangeEmailViewModel.cs | 13 +- .../ViewModels/ChangePasswordViewModel.cs | 25 +- .../ViewModels/ConfirmEmailViewModel.cs | 17 +- .../ViewModels/EditUserRoleViewModel.cs | 19 +- .../ViewModels/EditUserViewModel.cs | 29 +- .../ViewModels/ExternalLoginsViewModel.cs | 15 +- .../ViewModels/ForgotPasswordViewModel.cs | 17 +- .../ViewModels/LinkExternalLoginViewModel.cs | 13 +- .../ViewModels/LoginViewModel.cs | 21 +- .../ViewModels/LostPasswordViewModel.cs | 17 +- .../RegisterExternalLoginViewModel.cs | 81 +- .../ViewModels/RegisterViewModel.cs | 29 +- .../ViewModels/RemoveLoginViewModel.cs | 11 +- .../ViewModels/ResetPasswordViewModel.cs | 31 +- .../ViewModels/SummaryAdminUserViewModel.cs | 9 +- .../ViewModels/UsersIndexViewModel.cs | 25 +- .../Activities/AssignUserRoleTask.cs | 99 +- .../Workflows/Activities/RegisterUserTask.cs | 217 ++- .../Workflows/Activities/UserActivity.cs | 69 +- .../Workflows/Activities/UserCreatedEvent.cs | 15 +- .../Workflows/Activities/UserDeletedEvent.cs | 15 +- .../Workflows/Activities/UserDisabledEvent.cs | 15 +- .../Workflows/Activities/UserEnabledEvent.cs | 15 +- .../Workflows/Activities/UserEvent.cs | 25 +- .../Workflows/Activities/UserLoggedInEvent.cs | 15 +- .../Workflows/Activities/UserUpdatedEvent.cs | 15 +- .../Workflows/Activities/ValidateUserTask.cs | 99 +- .../AssignUserRoleTaskDisplayDriver.cs | 23 +- .../Drivers/RegisterUserTaskDisplayDriver.cs | 67 +- .../Drivers/UserCreatedEventDisplayDriver.cs | 29 +- .../Drivers/UserDeletedEventDisplayDriver.cs | 29 +- .../Drivers/UserDisabledEventDisplayDriver.cs | 29 +- .../Drivers/UserEnabledEventDisplayDriver.cs | 29 +- .../Drivers/UserLoggedInEventDisplayDriver.cs | 29 +- .../Drivers/UserUpdatedEventDisplayDriver.cs | 29 +- .../Drivers/ValidateUserTaskDisplayDriver.cs | 23 +- .../Workflows/Handlers/UserEventHandler.cs | 81 +- .../OrchardCore.Users/Workflows/Startup.cs | 41 +- .../ViewModels/AssignUserRoleTaskViewModel.cs | 15 +- .../ViewModels/RegisterUserTaskViewModel.cs | 15 +- .../ViewModels/UserCreatedEventViewModel.cs | 17 +- .../ViewModels/UserDeletedEventViewModel.cs | 17 +- .../ViewModels/UserDisabledEventViewModel.cs | 17 +- .../ViewModels/UserEnabledEventViewModel.cs | 17 +- .../ViewModels/UserEventViewModel.cs | 17 +- .../ViewModels/UserLoggedInEventViewModel.cs | 17 +- .../ViewModels/UserUpdatedEventViewModel.cs | 17 +- .../ViewModels/ValidateUserTaskViewModel.cs | 11 +- .../OrchardCore.Widgets/ContentCardShapes.cs | 257 ++- .../Controllers/AdminController.cs | 127 +- .../Drivers/WidgetsListPartDisplayDriver.cs | 169 +- .../OrchardCore.Widgets/Migrations.cs | 31 +- .../Models/WidgetMetadata.cs | 11 +- .../Models/WidgetsListPart.cs | 11 +- .../Settings/WidgetsListPartSettings.cs | 9 +- .../WidgetsListPartSettingsDisplayDriver.cs | 37 +- .../WidgetsListPartSettingsViewModel.cs | 11 +- .../OrchardCore.Widgets/Startup.cs | 29 +- .../ViewModels/BuildEditorViewModel.cs | 9 +- .../WidgetsListPartEditViewModel.cs | 25 +- .../Activities/CommitTransactionTask.cs | 57 +- .../Activities/CorrelateTask.cs | 80 +- .../Activities/ForEachTask.cs | 129 +- .../Activities/ForLoopTask.cs | 157 +- .../Activities/ForkTask.cs | 45 +- .../Activities/IfElseTask.cs | 67 +- .../Activities/JoinTask.cs | 167 +- .../Activities/LogTask.cs | 83 +- .../Activities/MissingActivity.cs | 53 +- .../Activities/NotifyTask.cs | 97 +- .../Activities/ScriptTask.cs | 83 +- .../Activities/SetOutputTask.cs | 77 +- .../Activities/SetPropertyTask.cs | 77 +- .../Activities/WhileLoopTask.cs | 67 +- .../OrchardCore.Workflows/AdminMenu.cs | 43 +- .../Controllers/ActivityController.cs | 271 ++- .../Controllers/WorkflowController.cs | 515 +++-- .../Controllers/WorkflowTypeController.cs | 893 +++++---- .../AllWorkflowTypeDeploymentSource.cs | 65 +- .../AllWorkflowTypeDeploymentStep.cs | 11 +- .../AllWorkflowTypeDeploymentStepDriver.cs | 27 +- .../Drivers/ActivityMetadataDisplayDriver.cs | 29 +- .../CommitTransactionTaskDisplayDriver.cs | 15 +- .../Drivers/CorrelateTaskDisplayDriver.cs | 23 +- .../Drivers/ForEachTaskDisplayDriver.cs | 23 +- .../Drivers/ForLoopTaskDisplayDriver.cs | 31 +- .../Drivers/ForkTaskDisplayDriver.cs | 19 +- .../Drivers/IfElseTaskDisplayDriver.cs | 19 +- .../Drivers/JoinTaskDisplayDriver.cs | 19 +- .../Drivers/LogTaskDisplayDriver.cs | 23 +- .../Drivers/MissingActivityDisplayDriver.cs | 13 +- .../Drivers/NotifyTaskDisplayDriver.cs | 23 +- .../Drivers/ScriptTaskDisplayDriver.cs | 23 +- .../Drivers/SetOutputTaskDisplayDriver.cs | 23 +- .../Drivers/SetVariableTaskDisplayDriver.cs | 23 +- .../Drivers/WhileLoopTaskDisplayDriver.cs | 19 +- .../WorkflowFaultEventDisplayDriver.cs | 19 +- .../Events/WorkflowFaultEvent.cs | 83 +- .../LiquidWorkflowExpressionEvaluator.cs | 77 +- .../DefaultWorkflowExecutionContextHandler.cs | 13 +- .../Handlers/DefaultWorkflowFaultHandler.cs | 51 +- .../Http/Activities/HttpRedirectTask.cs | 93 +- .../Http/Activities/HttpRequestEvent.cs | 147 +- .../Http/Activities/HttpRequestFilterEvent.cs | 153 +- .../Http/Activities/HttpRequestTask.cs | 365 ++-- .../Http/Activities/HttpResponseTask.cs | 159 +- .../Http/Activities/SignalEvent.cs | 73 +- .../Controllers/HttpWorkflowController.cs | 391 ++-- .../Drivers/HttpRedirectTaskDisplayDriver.cs | 23 +- .../Drivers/HttpRequestEventDisplayDriver.cs | 35 +- .../HttpRequestFilterEventDisplayDriver.cs | 39 +- .../Drivers/HttpRequestTaskDisplayDriver.cs | 39 +- .../Drivers/HttpResponseTaskDisplayDriver.cs | 31 +- .../Http/Drivers/SignalEventDisplayDriver.cs | 19 +- .../Http/Filters/WorkflowActionFilter.cs | 169 +- .../SignalWorkflowExecutionContextHandler.cs | 25 +- .../Http/Handlers/WorkflowRoutesHandler.cs | 79 +- .../Handlers/WorkflowTypeRoutesHandler.cs | 53 +- .../Http/Liquid/SignalUrlFilter.cs | 53 +- .../Http/Models/SignalPayload.cs | 39 +- .../Http/Models/WorkflowPayload.cs | 19 +- .../Http/Models/WorkflowRouteDocument.cs | 9 +- .../Http/Models/WorkflowRoutesEntry.cs | 21 +- .../Http/Models/WorkflowTypeRouteDocument.cs | 7 +- .../Http/Scripting/HttpMethodsProvider.cs | 336 ++-- .../Http/Scripting/SignalMethodProvider.cs | 85 +- .../Services/IWorkflowInstanceRouteEntries.cs | 7 +- .../Http/Services/IWorkflowRouteEntries.cs | 13 +- .../Services/IWorkflowTypeRouteEntries.cs | 7 +- .../Services/WorkflowInstanceRouteEntries.cs | 93 +- .../Http/Services/WorkflowRouteEntriesOfT.cs | 109 +- .../Http/Services/WorkflowTypeRouteEntries.cs | 57 +- .../OrchardCore.Workflows/Http/Startup.cs | 77 +- .../ViewModels/HttpRedirectTaskViewModel.cs | 13 +- .../ViewModels/HttpRequestEventViewModel.cs | 39 +- .../HttpRequestFilterEventViewModel.cs | 21 +- .../ViewModels/HttpRequestTaskViewModel.cs | 23 +- .../ViewModels/HttpResponseTaskViewModel.cs | 15 +- .../Indexes/WorkflowIndexProvider.cs | 89 +- .../Indexes/WorkflowTypeIndexProvider.cs | 85 +- .../OrchardCore.Workflows/Migrations.cs | 275 ++- .../OrchardCore.Workflows/Permissions.cs | 2 +- .../Recipes/WorkflowTypeStep.cs | 125 +- .../ResourceManagementOptionsConfiguration.cs | 49 +- .../JavaScriptWorkflowScriptEvaluator.cs | 53 +- .../Scripting/OutcomeMethodProvider.cs | 29 +- .../Scripting/WorkflowMethodsProvider.cs | 125 +- .../Services/ActivityDisplayManager.cs | 57 +- .../Services/ActivityIdGenerator.cs | 23 +- .../Services/ActivityLibrary.cs | 93 +- .../Services/LocalizedStringComparer.cs | 19 +- .../Services/Resolver.cs | 27 +- .../Services/SecurityTokenService.cs | 61 +- .../Services/WorkflowIdGenerator.cs | 23 +- .../Services/WorkflowManager.cs | 891 +++++---- .../Services/WorkflowStore.cs | 219 ++- .../Services/WorkflowTypeIdGenerator.cs | 23 +- .../Services/WorkflowTypeStore.cs | 121 +- .../OrchardCore.Workflows/Startup.cs | 117 +- .../OrchardCore.Workflows/Timers/Startup.cs | 15 +- .../Timers/TimerBackgroundTask.cs | 21 +- .../Timers/TimerEvent.cs | 91 +- .../Timers/TimerEventDisplayDriver.cs | 19 +- .../Timers/TimerEventViewModel.cs | 11 +- .../UserTasks/Activities/UserTaskEvent.cs | 91 +- .../Drivers/UserTaskEventContentDriver.cs | 187 +- .../Drivers/UserTaskEventDisplayDriver.cs | 23 +- .../UserTasks/Startup.cs | 15 +- .../UserTaskEventContentViewModel.cs | 9 +- .../ViewModels/UserTaskEventViewModel.cs | 11 +- .../SelectWorkflowTypeViewComponent.cs | 33 +- .../ViewModels/ActivityEditViewModel.cs | 17 +- .../ActivityMetadataEditViewModel.cs | 9 +- .../CommitTransactionTaskViewModel.cs | 7 +- .../ViewModels/CorrelateTaskViewModel.cs | 11 +- .../ViewModels/ForEachTaskViewModel.cs | 15 +- .../ViewModels/ForLoopTaskViewModel.cs | 15 +- .../ViewModels/ForkTaskViewModel.cs | 9 +- .../ViewModels/IfElseTaskViewModel.cs | 9 +- .../ViewModels/JoinTaskViewModel.cs | 9 +- .../ViewModels/LogTaskViewModel.cs | 13 +- .../ViewModels/NotifyTaskViewModel.cs | 13 +- .../ViewModels/ScriptTaskViewModel.cs | 15 +- .../ViewModels/SelectWorkflowTypeViewModel.cs | 45 +- .../ViewModels/SetOutputTaskViewModel.cs | 13 +- .../ViewModels/SetPropertyTaskViewModel.cs | 13 +- .../ViewModels/SignalEventViewModel.cs | 11 +- .../ViewModels/WhileLoopTaskViewModel.cs | 11 +- .../ViewModels/WorkflowFaultViewModel.cs | 11 +- .../ViewModels/WorkflowIndexViewModel.cs | 97 +- .../ViewModels/WorkflowTypeIndexViewModel.cs | 89 +- .../WorkflowTypePropertiesViewModel.cs | 25 +- .../ViewModels/WorkflowTypeUpdateModel.cs | 11 +- .../ViewModels/WorkflowTypeViewModel.cs | 25 +- .../ViewModels/WorkflowViewModel.cs | 17 +- .../WorkflowPruning/AdminMenu.cs | 2 +- .../Drivers/WorkflowPruningDisplayDriver.cs | 2 +- .../Models/WorkflowPruningSettings.cs | 2 +- .../Controllers/HomeController.cs | 125 +- .../Controllers/MetaWeblogController.cs | 77 +- .../MethodCallModelBinder.cs | 33 +- .../Services/XmlRpcReader.cs | 215 +- .../Services/XmlRpcWriter.cs | 195 +- .../OrchardCore.XmlRpc/Startup.cs | 53 +- .../ResourceManagementOptionsConfiguration.cs | 51 +- src/OrchardCore.Themes/TheAdmin/Startup.cs | 25 +- .../ResourceManagementOptionsConfiguration.cs | 69 +- .../TheAgencyTheme/Startup.cs | 11 +- .../ResourceManagementOptionsConfiguration.cs | 69 +- .../TheBlogTheme/Startup.cs | 11 +- .../ResourceManagementOptionsConfiguration.cs | 67 +- .../TheComingSoonTheme/Startup.cs | 11 +- .../TheTheme/Controllers/HomeController.cs | 11 +- .../BackgroundJobs/HttpBackgroundJob.cs | 2 +- .../BackgroundTaskAttribute.cs | 83 +- .../BackgroundTaskEventContext.cs | 27 +- .../BackgroundTaskExtensions.cs | 53 +- .../BackgroundTasks/BackgroundTaskSettings.cs | 103 +- .../BackgroundTasks/BackgroundTaskState.cs | 11 +- .../DistributedLockExtensions.cs | 29 +- .../BackgroundTasks/IBackgroundTask.cs | 9 +- .../IBackgroundTaskEventHandler.cs | 27 +- .../IBackgroundTaskSettingsProvider.cs | 11 +- .../Caching/Distributed/IMessageBus.cs | 11 +- .../Caching/ISignal.cs | 27 +- .../Extensions/ExtensionEntry.cs | 15 +- .../ExtensionsEnvironmentExtensions.cs | 19 +- .../Features/IFeatureBuilderEvents.cs | 53 +- .../Extensions/Features/IFeatureHash.cs | 47 +- .../Extensions/Features/IFeatureInfo.cs | 27 +- .../Extensions/Features/IFeaturesProvider.cs | 27 +- .../Features/ITypeFeatureProvider.cs | 67 +- .../IExtensionDependencyStrategy.cs | 9 +- .../Extensions/IExtensionInfo.cs | 47 +- .../Extensions/IExtensionManager.cs | 25 +- .../Extensions/IExtensionPriorityStrategy.cs | 9 +- .../Extensions/Manifests/IManifestInfo.cs | 25 +- .../Manifests/NotFoundManifestInfo.cs | 25 +- .../Extensions/NotFoundExtensionInfo.cs | 29 +- .../Extensions/Utility/DependencyOrdering.cs | 127 +- .../IOrchardHelper.cs | 9 +- .../OrchardCore.Abstractions/IdGenerator.cs | 83 +- .../Json/Nodes/JNode.cs | 4 +- .../Localization/CalendarName.cs | 27 +- .../Localization/CalendarSelectorResult.cs | 25 +- .../Localization/CultureScope.cs | 137 +- .../Localization/ICalendarManager.cs | 19 +- .../Localization/ICalendarSelector.cs | 19 +- .../Localization/ILocalizationService.cs | 55 +- .../Locking/Distributed/IDistributedLock.cs | 7 +- .../Locking/ILocalLock.cs | 7 +- .../OrchardCore.Abstractions/Locking/ILock.cs | 35 +- .../Locking/ILocker.cs | 7 +- .../Modules/Application.cs | 77 +- .../AssemblyAttributeModuleNamesProvider.cs | 25 +- .../OrchardCore.Abstractions/Modules/Asset.cs | 37 +- .../Modules/BackgroundServiceOptions.cs | 9 +- .../Modules/Builder/OrchardCoreBuilder.cs | 233 ++- .../Modules/Builder/StartupActions.cs | 21 +- .../Modules/Builder/StartupActionsStartup.cs | 65 +- .../Modules/Exceptions/ExceptionExtensions.cs | 35 +- .../Modules/Extensions/InvokeExtensions.cs | 419 ++-- .../Modules/Extensions/StringExtensions.cs | 31 +- .../Modules/FeatureAttribute.cs | 33 +- .../EmbeddedDirectoryContents.cs | 41 +- .../FileProviders/EmbeddedDirectoryInfo.cs | 97 +- .../FileProviders/FileInfoExtensions.cs | 33 +- .../FileProviders/IStaticFileProvider.cs | 15 +- .../FileProviders/IVirtualPathBaseProvider.cs | 9 +- .../Modules/FileProviders/NormalizedPaths.cs | 65 +- .../Modules/IAsyncStartup.cs | 23 +- .../Modules/IModularTenantEvents.cs | 53 +- .../Modules/IModuleNamesProvider.cs | 9 +- .../Modules/IModuleStaticFileProvider.cs | 15 +- .../Modules/IStartup.cs | 53 +- .../Modules/Manifest/FeatureAttribute.cs | 609 +++--- .../Modules/Manifest/ModuleAssetAttribute.cs | 29 +- .../Modules/Manifest/ModuleAttribute.cs | 637 +++--- .../Modules/Manifest/ModuleMarkerAttribute.cs | 33 +- .../Modules/Manifest/ModuleNameAttribute.cs | 29 +- .../Modules/Model/ITimeZone.cs | 15 +- .../Modules/ModularApplicationContext.cs | 77 +- .../Modules/Module.cs | 245 ++- .../ModuleCompositeStaticFileProvider.cs | 23 +- .../Modules/ModuleEmbeddedFileProvider.cs | 157 +- .../ModuleEmbeddedStaticFileProvider.cs | 89 +- .../ModuleProjectStaticFileProvider.cs | 181 +- .../TenantJsonConfigurationExtensions.cs | 171 +- .../TenantJsonConfigurationProvider.cs | 29 +- .../TenantJsonConfigurationSource.cs | 25 +- .../TenantJsonStreamConfigurationProvider.cs | 33 +- .../TenantJsonStreamConfigurationSource.cs | 23 +- .../Modules/RequireFeaturesAttribute.cs | 51 +- .../Modules/Services/ClockExtensions.cs | 55 +- .../Modules/Services/IClock.cs | 61 +- .../Modules/Services/ILocalClock.cs | 41 +- .../Modules/Services/ISlugService.cs | 35 +- .../Modules/Services/ITimeZoneSelector.cs | 15 +- .../Services/TimeZoneSelectorResult.cs | 11 +- .../Modules/ShellRequestPipeline.cs | 11 +- .../Modules/StartupBase.cs | 35 +- .../Routing/FormValueRequiredAttribute.cs | 17 +- .../Routing/FormValueRequiredMatcherPolicy.cs | 85 +- .../Routing/IShellRouteValuesAddressScheme.cs | 13 +- .../Routing/PathStringExtensions.cs | 59 +- .../Routing/ShellRouteValuesAddressScheme.cs | 77 +- .../Setup/SetupConstants.cs | 31 +- .../Extensions/ClonedSingletonDescriptor.cs | 47 +- .../Extensions/ServiceDescriptorExtensions.cs | 105 +- .../Extensions/ServiceProviderExtensions.cs | 53 +- .../Shell/Builders/ICompositionStrategy.cs | 19 +- .../Shell/Builders/IShellContainerFactory.cs | 9 +- .../Shell/Builders/IShellContextFactory.cs | 39 +- .../Shell/Builders/IShellPipeline.cs | 15 +- .../Shell/Builders/Models/ShellBlueprint.cs | 23 +- .../Shell/Builders/ShellContext.cs | 445 +++-- .../Shell/Builders/ShellContextExtensions.cs | 91 +- .../Shell/Builders/ShellContextOptions.cs | 51 +- .../ConfigurationSectionExtensions.cs | 85 +- .../Configuration/IShellConfiguration.cs | 7 +- .../IShellConfigurationSources.cs | 25 +- .../IShellsConfigurationSources.cs | 21 +- .../Configuration/IShellsSettingsSources.cs | 35 +- .../Internal/ConfigurationExtensions.cs | 2 +- .../Internal/UpdatableDataProvider.cs | 69 +- .../Shell/Configuration/ShellConfiguration.cs | 219 ++- .../Descriptor/IShellDescriptorManager.cs | 33 +- .../Descriptor/Models/ShellDescriptor.cs | 37 +- .../Shell/Descriptor/Models/ShellFeature.cs | 55 +- .../DistributedShellMarkerService.cs | 7 +- .../Shell/Events/IShellEvents.cs | 39 +- .../Shell/Events/ShellEvent.cs | 13 +- .../Shell/Events/ShellsEvent.cs | 13 +- .../ShellFeaturesManagerExtensions.cs | 81 +- .../Shell/Extensions/ShellHostExtensions.cs | 109 +- .../Shell/IFeatureProfilesService.cs | 9 +- .../Shell/IFeatureValidationProvider.cs | 15 +- .../Shell/IRunningShellTable.cs | 13 +- .../Shell/IShellDescriptorFeaturesManager.cs | 15 +- .../IShellDescriptorManagerEventHandler.cs | 17 +- .../Shell/IShellFeaturesManager.cs | 21 +- .../Shell/IShellHost.cs | 123 +- .../Shell/IShellSettingsManager.cs | 65 +- .../Shell/Models/FeatureProfile.cs | 27 +- .../Models/FeatureProfilesRuleOptions.cs | 15 +- .../Shell/Models/TenantState.cs | 49 +- .../Shell/Scope/ShellScope.cs | 959 +++++---- .../Shell/Scope/ShellScopeExtensions.cs | 153 +- .../Shell/ShellContextFeature.cs | 33 +- .../Shell/ShellHostReloadException.cs | 21 +- .../Shell/ShellOptions.cs | 23 +- .../Shell/ShellSettings.cs | 301 ++- .../StringUriExtensions.cs | 49 +- .../AdminAttribute.cs | 97 +- .../AdminOptions.cs | 17 +- .../Constants.cs | 9 +- .../IAdminThemeService.cs | 13 +- .../Models/AdminMenu.cs | 85 +- .../Models/AdminNode.cs | 119 +- .../Services/IAdminMenuPermissionService.cs | 9 +- .../Services/IAdminNodeNavigationBuilder.cs | 15 +- .../Services/IAdminNodeProviderFactory.cs | 27 +- .../FieldBuilderResolverExtensions.cs | 11 +- .../Extensions/PermissionsExtensions.cs | 53 +- .../ResolveFieldContextExtensions.cs | 93 +- .../GraphQLFieldNameAttribute.cs | 21 +- .../GraphQLPermissionContext.cs | 19 +- .../GraphQLSettings.cs | 37 +- .../GraphQLUserContext.cs | 11 +- .../ISchemaBuilder.cs | 29 +- .../ISchemaFactory.cs | 17 +- .../Queries/INamedQueryProvider.cs | 9 +- .../Queries/Types/TimeSpanGraphType.cs | 67 +- .../Queries/WhereInputObjectGraphType.cs | 199 +- .../Resolvers/LockedAsyncFieldResolver.cs | 63 +- .../ContentPartBuilder.cs | 73 +- .../ContentResource.cs | 93 +- .../ContentTypeCreateResourceBuilder.cs | 89 +- .../ContentTypeQueryResourceBuilder.cs | 267 ++- .../Extensions/HttpContentExtensions.cs | 19 +- .../Extensions/HttpRequestExtensions.cs | 367 ++-- .../OrchardGraphQLClient.cs | 13 +- .../ServiceExtensions.cs | 91 +- .../AuditTrailEventSectionDisplayDriver.cs | 25 +- .../Indexes/AuditTrailEventIndex.cs | 27 +- .../Models/AuditTrailEvent.cs | 105 +- .../Services/AuditTrailEventHandlerBase.cs | 11 +- .../AuditTrailFilterEngineModelBinder.cs | 49 +- .../Services/AuditTrailQueryContext.cs | 15 +- .../IAuditTrailAdminListFilterParser.cs | 7 +- .../IAuditTrailAdminListFilterProvider.cs | 9 +- .../IAuditTrailAdminListQueryService.cs | 21 +- .../Services/IAuditTrailEventHandler.cs | 25 +- .../Services/IAuditTrailIdGenerator.cs | 9 +- .../Services/IAuditTrailManager.cs | 75 +- .../Models/AuditTrailAdminListOption.cs | 39 +- .../AuditTrailAdminListOptionBuilder.cs | 101 +- .../Models/AuditTrailAdminListOptions.cs | 57 +- .../Models/AuditTrailCategoryDescriptor.cs | 45 +- .../AuditTrailCategoryDescriptorBuilder.cs | 99 +- .../Services/Models/AuditTrailContext.cs | 69 +- .../Models/AuditTrailCreateContext.cs | 53 +- .../Models/AuditTrailEventDescriptor.cs | 55 +- .../Models/AuditTrailEventQueryResult.cs | 11 +- .../Services/Models/AuditTrailOptions.cs | 47 +- .../Settings/AuditTrailSettingsGroup.cs | 9 +- .../ViewModels/AuditTrailIndexOptions.cs | 91 +- .../Indexes/AutoroutePartIndex.cs | 285 ++- .../Model/AutoroutePart.cs | 59 +- .../Model/AutorouteStateDocument.cs | 7 +- .../Services/AutorouteEntries.cs | 341 ++-- .../Services/AutorouteHandleProvider.cs | 45 +- .../AzureKeyVaultConfigurationExtension.cs | 135 +- .../Services/AzureKeyVaultSecretManager.cs | 11 +- .../CultureAspect.cs | 19 +- .../ContentLocalizationHandlerBase.cs | 19 +- .../ContentLocalizationPartHandlerBase.cs | 27 +- .../Handlers/IContentLocalizationHandler.cs | 11 +- .../IContentLocalizationPartHandler.cs | 11 +- .../Handlers/LocalizationContentContext.cs | 23 +- .../IContentLocalizationManager.cs | 91 +- .../ILocalizable.cs | 11 +- .../Models/LocalizationEntry.cs | 15 +- .../Models/LocalizationPart.cs | 11 +- .../Records/LocalizedContentItemIndex.cs | 147 +- .../ContentDefinitionManagerExtensions.cs | 191 +- .../ContentExtensions.cs | 503 +++-- .../ContentField.cs | 7 +- .../ContentFieldOption.cs | 31 +- .../ContentFieldOptionBase.cs | 17 +- .../ContentFieldOptionBuilder.cs | 19 +- .../ContentItem.cs | 123 +- .../ContentItemConverter.cs | 171 +- .../ContentItemExtensions.cs | 401 ++-- .../ContentItemMetadata.cs | 21 +- .../ContentOptions.cs | 115 +- .../ContentPart.cs | 7 +- .../ContentPartOption.cs | 31 +- .../ContentPartOptionBase.cs | 17 +- .../ContentPartOptionBuilder.cs | 19 +- .../ContentQueryExtensions.cs | 59 +- .../ContentQueryOfTIndexExtensions.cs | 65 +- .../GroupInfo.cs | 31 +- .../Handlers/ActivatedContentContext.cs | 9 +- .../Handlers/ActivatingContentContext.cs | 15 +- .../Handlers/CloneContentContext.cs | 17 +- .../Handlers/ContentContextBase.cs | 15 +- .../Handlers/ContentFieldContextBase.cs | 19 +- .../Handlers/ContentHandlerBase.cs | 289 ++- .../Handlers/ContentItemAspectContext.cs | 31 +- .../Handlers/ContentItemMetadataContext.cs | 11 +- .../Handlers/ContentPartHandler.cs | 403 ++-- .../Handlers/ContentValidateResult.cs | 29 +- .../Handlers/CreateContentContext.cs | 9 +- .../Handlers/IContentFieldHandler.cs | 67 +- .../Handlers/IContentFieldHandlerResolver.cs | 9 +- .../Handlers/IContentHandler.cs | 65 +- .../Handlers/IContentPartHandler.cs | 67 +- .../Handlers/IContentPartHandlerResolver.cs | 9 +- .../Handlers/ImportContentContext.cs | 21 +- .../Handlers/InitializingContentContext.cs | 9 +- .../Handlers/LoadContentContext.cs | 9 +- .../Handlers/PublishContentContext.cs | 21 +- .../Handlers/RemoveContentContext.cs | 15 +- .../Handlers/RestoreContentContext.cs | 9 +- .../Handlers/SaveDraftContentContext.cs | 9 +- .../Handlers/UpdateContentContext.cs | 15 +- .../Handlers/ValidateContentContext.cs | 37 +- .../Handlers/VersionContentContext.cs | 15 +- .../IContent.cs | 9 +- .../IContentDefinitionStore.cs | 31 +- .../IContentHandleManager.cs | 9 +- .../IContentHandleProvider.cs | 11 +- .../IContentItemIdGenerator.cs | 9 +- .../IContentManager.cs | 261 ++- .../IContentManagerSession.cs | 15 +- .../IContentPickerResultProvider.cs | 37 +- .../ITypeActivatorFactory.cs | 37 +- .../IUserPickerResultProvider.cs | 37 +- .../Builders/ContentBuilderSettings.cs | 41 +- .../Builders/ContentPartDefinitionBuilder.cs | 369 ++-- .../ContentPartFieldDefinitionBuilder.cs | 95 +- .../Builders/ContentTypeDefinitionBuilder.cs | 317 ++- .../ContentTypePartDefinitionBuilder.cs | 85 +- .../Metadata/Models/ContentDefinition.cs | 69 +- .../Metadata/Models/ContentFieldDefinition.cs | 15 +- .../Metadata/Models/ContentPartDefinition.cs | 37 +- .../Models/ContentPartFieldDefinition.cs | 23 +- .../ContentPartFieldDefinitionExtensions.cs | 11 +- .../ContentPartFieldSettingsExtensions.cs | 45 +- .../Metadata/Models/ContentTypeDefinition.cs | 65 +- .../Models/ContentTypePartDefinition.cs | 27 +- .../Models/ContentTypePartExtensions.cs | 71 +- .../Records/ContentDefinitionRecord.cs | 21 +- .../Records/ContentFieldDefinitionRecord.cs | 15 +- .../Records/ContentPartDefinitionRecord.cs | 29 +- .../ContentPartFieldDefinitionRecord.cs | 33 +- .../Records/ContentTypeDefinitionRecord.cs | 21 +- .../ContentTypePartDefinitionRecord.cs | 33 +- .../Settings/ContentPartFieldSettings.cs | 47 +- .../ContentPartFieldSettingsExtensions.cs | 43 +- .../Metadata/Settings/ContentPartSettings.cs | 47 +- .../Settings/ContentPartSettingsExtensions.cs | 93 +- .../Settings/ContentTypePartSettings.cs | 47 +- .../ContentTypePartSettingsExtensions.cs | 43 +- .../Metadata/Settings/ContentTypeSettings.cs | 63 +- .../Settings/ContentTypeSettingsExtensions.cs | 71 +- .../Models/BodyAspect.cs | 9 +- .../Models/FullTextAspect.cs | 15 +- .../PreviewAspect.cs | 9 +- .../Routing/AutorouteEntry.cs | 75 +- .../Routing/AutorouteOptions.cs | 17 +- .../Routing/ContainedContentItemsAspect.cs | 15 +- .../Routing/IAutorouteEntries.cs | 13 +- .../Routing/RouteHandlerAspect.cs | 31 +- .../ServiceCollectionExtensions.cs | 257 ++- .../Utilities/StringExtensions.cs | 527 +++-- .../Workflows/ContentEventConstants.cs | 13 +- .../Workflows/ContentEventContext.cs | 17 +- .../ContentDisplay/ContentDisplayOptions.cs | 169 +- .../ContentFieldDisplayDriver.cs | 429 ++-- .../ContentFieldDisplayDriverOption.cs | 33 +- .../ContentFieldDisplayDriverResolver.cs | 67 +- .../ContentFieldDisplayOption.cs | 73 +- ...ContentFieldServiceCollectionExtensions.cs | 289 ++- .../ContentItemDisplayCoordinator.cs | 569 +++--- .../ContentPartDisplayDriverOption.cs | 33 +- .../ContentPartDisplayDriverResolver.cs | 67 +- .../ContentPartDisplayDriverTPart.cs | 509 +++-- .../ContentPartDisplayOption.cs | 77 +- .../ContentPartServiceCollectionExtensions.cs | 277 ++- .../ContentDisplay/IContentDisplayDriver.cs | 7 +- .../ContentDisplay/IContentDisplayHandler.cs | 21 +- .../IContentFieldDisplayDriver.cs | 13 +- .../IContentFieldDisplayDriverResolver.cs | 11 +- .../IContentPartDisplayDriver.cs | 13 +- .../IContentPartDisplayDriverResolver.cs | 11 +- .../ContentItemDisplayManager.cs | 315 ++- .../IContentItemDisplayManager.cs | 21 +- .../Liquid/ConsoleLogFilter.cs | 91 +- .../Models/BuildFieldDisplayContext.cs | 25 +- .../Models/BuildFieldEditorContext.cs | 25 +- .../Models/BuildPartDisplayContext.cs | 17 +- .../Models/BuildPartEditorContext.cs | 19 +- .../Models/UpdateFieldEditorContext.cs | 11 +- .../Models/UpdatePartEditorContext.cs | 15 +- .../ContentPartPlacementNodeFilterProvider.cs | 137 +- .../ServiceCollectionExtensions.cs | 35 +- .../ViewModels/ContentItemViewModel.cs | 21 +- .../ViewModels/ContentPartViewModel.cs | 21 +- .../Extensions/DataLoaderExtensions.cs | 31 +- .../Extensions/StringExtensions.cs | 11 +- .../Options/GraphQLContentOptions.cs | 297 ++- .../Options/GraphQLContentPartOption.cs | 29 +- .../Options/GraphQLContentTypeOption.cs | 53 +- .../Options/GraphQLField.cs | 31 +- .../Queries/ContentItemQuery.cs | 77 +- .../Queries/ContentItemsFieldType.cs | 583 +++--- .../Queries/ContentTypeQuery.cs | 137 +- .../Queries/GraphQLFilter.cs | 19 +- .../Queries/IGraphQLFilter.cs | 11 +- .../Queries/IIndexAliasProvider.cs | 9 +- .../Queries/IIndexPropertyProvider.cs | 11 +- .../Queries/IndexAlias.cs | 13 +- .../Queries/IndexPropertyProvider.cs | 33 +- .../Queries/Predicates/AndExpression.cs | 33 +- .../Queries/Predicates/Conjunction.cs | 21 +- .../Queries/Predicates/Disjunction.cs | 21 +- .../Queries/Predicates/Expression.cs | 235 ++- .../Queries/Predicates/IPredicate.cs | 19 +- .../Queries/Predicates/IPredicateQuery.cs | 95 +- .../Queries/Predicates/InExpression.cs | 67 +- .../Queries/Predicates/Junction.cs | 99 +- .../Queries/Predicates/LikeExpression.cs | 53 +- .../Queries/Predicates/LogicalExpression.cs | 75 +- .../Queries/Predicates/MatchOptions.cs | 103 +- .../Queries/Predicates/NotExpression.cs | 43 +- .../Queries/Predicates/OrExpression.cs | 33 +- .../Queries/Predicates/PredicateQuery.cs | 205 +- .../Queries/Predicates/SimpleExpression.cs | 77 +- .../Queries/PublicationStatusEnum.cs | 15 +- .../Queries/Types/ContentItemInterface.cs | 53 +- .../Queries/Types/ContentItemOrderByInput.cs | 59 +- .../Queries/Types/ContentItemType.cs | 95 +- .../Queries/Types/ContentItemWhereInput.cs | 59 +- .../Types/DynamicContentTypeBuilder.cs | 265 ++- .../Queries/Types/DynamicPartGraphType.cs | 47 +- .../Queries/Types/IContentFieldProvider.cs | 25 +- .../Queries/Types/IContentTypeBuilder.cs | 11 +- .../Queries/Types/TypedContentTypeBuilder.cs | 203 +- .../ServiceCollectionExtensions.cs | 81 +- .../GraphQLContentTypePartSettings.cs | 13 +- .../ContentDefinitionCacheContextProvider.cs | 29 +- .../PublishedContentItemById.cs | 25 +- .../ContentDefinitionManager.cs | 589 +++--- .../ContentFieldFactory.cs | 53 +- .../ContentHandleManager.cs | 33 +- .../ContentPartFactory.cs | 53 +- .../DatabaseContentDefinitionStore.cs | 29 +- .../DefaultContentItemIdGenerator.cs | 23 +- .../DefaultContentManager.cs | 1725 ++++++++--------- .../DefaultContentManagerSession.cs | 79 +- .../FileContentDefinitionStore.cs | 29 +- .../GenericTypeActivator.cs | 19 +- .../Handlers/ContentFieldHandlerResolver.cs | 43 +- .../Handlers/ContentPartHandlerCoordinator.cs | 777 ++++---- .../Handlers/ContentPartHandlerResolver.cs | 43 +- .../Handlers/UpdateContentsHandler.cs | 121 +- .../Records/ContentItemIndex.cs | 119 +- .../Records/Migrations.cs | 397 ++-- .../ServiceCollectionExtensions.cs | 55 +- .../ContentPreviewFeature.cs | 15 +- .../ContentPartDefinitionDisplayDriver.cs | 11 +- ...ContentPartFieldDefinitionDisplayDriver.cs | 57 +- .../ContentTypeDefinitionDisplayDriver.cs | 11 +- .../ContentTypePartDefinitionDisplayDriver.cs | 49 +- .../IContentDefinitionDisplayHandler.cs | 23 +- .../IContentDefinitionDisplayManager.cs | 23 +- .../IContentPartDefinitionDisplayDriver.cs | 7 +- ...ContentPartFieldDefinitionDisplayDriver.cs | 7 +- .../IContentTypeDefinitionDisplayDriver.cs | 7 +- ...IContentTypePartDefinitionDisplayDriver.cs | 7 +- .../UpdateContentDefinitionEditorContext.cs | 31 +- .../Editors/UpdatePartEditorContext.cs | 25 +- .../Editors/UpdatePartFieldEditorContext.cs | 25 +- .../Editors/UpdateTypeEditorContext.cs | 25 +- .../Editors/UpdateTypePartEditorContext.cs | 25 +- .../Events/ContentFieldAttachedContext.cs | 11 +- .../Events/ContentFieldDetachedContext.cs | 7 +- .../Events/ContentFieldUpdatedContext.cs | 7 +- .../Events/ContentPartAttachedContext.cs | 7 +- .../Events/ContentPartContext.cs | 9 +- .../Events/ContentPartCreatedContext.cs | 7 +- .../Events/ContentPartDetachedContext.cs | 7 +- .../Events/ContentPartFieldContext.cs | 11 +- .../Events/ContentPartFieldUpdatedContext.cs | 7 +- .../Events/ContentPartImportedContext.cs | 7 +- .../Events/ContentPartImportingContext.cs | 9 +- .../Events/ContentPartRemovedContext.cs | 7 +- .../Events/ContentPartUpdatedContext.cs | 7 +- .../Events/ContentTypeContext.cs | 9 +- .../Events/ContentTypeCreatedContext.cs | 7 +- .../Events/ContentTypeImportedContext.cs | 7 +- .../Events/ContentTypeImportingContext.cs | 9 +- .../Events/ContentTypePartContext.cs | 11 +- .../Events/ContentTypePartUpdatedContext.cs | 7 +- .../Events/ContentTypeRemovedContext.cs | 7 +- .../Events/ContentTypeUpdatedContext.cs | 7 +- .../Events/IContentDefinitionEventHandler.cs | 41 +- .../AuthorizationServiceExtensions.cs | 137 +- .../Indexing/IndexingConstants.cs | 51 +- .../ContentItemFilterEngineModelBinder.cs | 49 +- .../Services/ContentQueryContext.cs | 15 +- .../Services/IContentsAdminListFilter.cs | 19 +- .../IContentsAdminListFilterParser.cs | 7 +- .../IContentsAdminListFilterProvider.cs | 15 +- .../IContentsAdminListQueryService.cs | 9 +- .../ViewModels/ContentOptionsViewModel.cs | 125 +- .../ContentItemTagHelper.cs | 15 +- .../ContentLinkTagHelper.cs | 303 ++- .../DatabaseProvider.cs | 19 +- .../Documents/Document.cs | 25 +- .../Documents/DocumentStoreCommitException.cs | 39 +- .../DocumentStoreCommitFailureDelegate.cs | 13 +- .../DocumentStoreCommitSuccessDelegate.cs | 13 +- .../Documents/FileDocumentStoreAttribute.cs | 11 +- .../Documents/IDocument.cs | 23 +- .../Documents/IDocumentStore.cs | 85 +- .../Documents/IFileDocumentStore.cs | 15 +- .../IDataMigrationManager.cs | 57 +- .../IDbConnectionAccessor.cs | 17 +- .../ServiceCollectionExtensions.cs | 53 +- .../StoreCollectionOptions.cs | 9 +- .../DataMigration.cs | 47 +- .../IDataMigration.cs | 19 +- .../IScopedIndexProvider.cs | 15 +- .../DbConnectionAccessor.cs | 37 +- .../DefaultContentJsonSerializer.cs | 33 +- .../Documents/DocumentStore.cs | 199 +- .../Migration/DataMigrationManager.cs | 485 +++-- .../Options/SqliteOptions.cs | 29 +- .../Options/SqliteOptionsConfiguration.cs | 25 +- .../OrchardCoreBuilderExtensions.cs | 317 ++- .../OrchardCore.Data.YesSql/SqliteHelper.cs | 3 +- .../Documents/FileDocumentStore.cs | 195 +- .../Migration/AutomaticDataMigrations.cs | 63 +- .../Migration/Records/DataMigration.cs | 25 +- .../Migration/Records/DataMigrationRecord.cs | 37 +- .../DeploymentPlan.cs | 19 +- .../DeploymentPlanResult.cs | 59 +- .../DeploymentStep.cs | 11 +- .../DeploymentTarget.cs | 23 +- .../IDeploymentPlanService.cs | 19 +- .../IDeploymentSource.cs | 15 +- .../IDeploymentStepFactory.cs | 27 +- .../IDeploymentTargetHandler.cs | 9 +- .../IDeploymentTargetProvider.cs | 9 +- .../IFileBuilder.cs | 21 +- .../Services/IDeploymentManager.cs | 13 +- .../Mvc/DeleteFileResultFilter.cs | 27 +- .../ServiceCollectionExtensions.cs | 13 +- .../Services/DeploymentManager.cs | 73 +- .../Services/TemporaryFileBuilder.cs | 67 +- .../ModelBinding/IUpdateModel.cs | 19 +- .../ModelBinding/IUpdateModelAccessor.cs | 9 +- .../ModelStateDictionaryExtensions.cs | 71 +- .../TagHelpers/AssemblyTagHelpersProvider.cs | 29 +- .../Cache/CacheDependencyTag.cs | 27 +- .../Cache/CacheExpiresAfterTag.cs | 55 +- .../Cache/CacheExpiresOnTag.cs | 73 +- .../Cache/CacheExpiresSlidingTag.cs | 55 +- .../Cache/CacheTag.cs | 183 +- .../Filters/AppendVersionFilter.cs | 31 +- .../Filters/LiquidViewFilters.cs | 65 +- .../Filters/NewShapeFilter.cs | 51 +- .../Filters/ResourceUrlFilter.cs | 59 +- .../Filters/SanitizeHtmlFilter.cs | 27 +- .../Filters/ShapeRenderFilter.cs | 39 +- .../Filters/ShapeStringifyFilter.cs | 47 +- .../HtmlContentValue.cs | 139 +- .../LiquidCultureAccessor.cs | 13 +- .../LiquidHttpContextAccessor.cs | 13 +- .../LiquidPage.cs | 9 +- .../LiquidRequestAccessor.cs | 13 +- .../LiquidShapeTemplateOptionsSetup.cs | 23 +- .../LiquidViewExtensionProvider.cs | 9 +- .../LiquidViewOptions.cs | 29 +- .../LiquidViewOptionsSetup.cs | 57 +- .../LiquidViewParser.cs | 123 +- .../LiquidViewTemplate.cs | 407 ++-- .../LiquidViewsFeatureProvider.cs | 73 +- .../ModuleProjectLiquidFileProvider.cs | 129 +- .../OrchardCoreBuilderExtensions.cs | 321 ++- .../TagHelpers/LiquidTagHelperActivator.cs | 359 ++-- .../TagHelpers/LiquidTagHelperFactory.cs | 181 +- .../TagHelpers/LiquidTagHelperMatching.cs | 107 +- .../TagHelpers/RequiredAttributeParser.cs | 417 ++-- .../Tags/AddAlternatesTag.cs | 39 +- .../Tags/AddAttributesTag.cs | 25 +- .../Tags/AddClassesTag.cs | 39 +- .../Tags/AddWrappersTag.cs | 39 +- .../Tags/AnchorTag.cs | 313 ++- .../Tags/AntiForgeryTokenTag.cs | 31 +- .../Tags/ClearAlternatesTag.cs | 21 +- .../Tags/ClearAttributesTag.cs | 21 +- .../Tags/ClearClassesTag.cs | 21 +- .../Tags/ClearWrappersTag.cs | 21 +- .../Tags/DefaultAnchorTag.cs | 25 +- .../Tags/FluidTagHelper.cs | 149 +- .../Tags/HttpContextAddItemTag.cs | 23 +- .../Tags/HttpContextRemoveItemTag.cs | 29 +- .../Tags/IAnchorTag.cs | 13 +- .../Tags/LayoutTag.cs | 25 +- .../Tags/RenderBodyTag.cs | 21 +- .../Tags/RenderSectionTag.cs | 45 +- .../Tags/RenderTitleSegmentsTag.cs | 33 +- .../Tags/ShapeAddPropertyTag.cs | 25 +- .../Tags/ShapeCacheTag.cs | 71 +- .../Tags/ShapeDisplayTypeTag.cs | 23 +- .../Tags/ShapePagerTag.cs | 149 +- .../Tags/ShapePositionTag.cs | 23 +- .../Tags/ShapeRemoveItemTag.cs | 23 +- .../Tags/ShapeRemovePropertyTag.cs | 25 +- .../Tags/ShapeTabTag.cs | 23 +- .../Tags/ShapeTag.cs | 147 +- .../Tags/ShapeTypeTag.cs | 23 +- .../Tags/ZoneTag.cs | 113 +- .../TemplateOptionsFileProviderSetup.cs | 41 +- .../ViewBufferTextWriterContent.cs | 471 +++-- .../Arguments.cs | 307 ++- .../BaseDisplayManager.cs | 89 +- .../Descriptors/DefaultShapeTableManager.cs | 287 ++- .../Descriptors/FeatureShapeDescriptor.cs | 229 ++- .../Descriptors/PlacementInfo.cs | 237 ++- .../Descriptors/PlacementInfoExtensions.cs | 105 +- .../Descriptors/ShapeAlteration.cs | 33 +- .../Descriptors/ShapeAlterationBuilder.cs | 349 ++-- .../ShapeAttributeBindingStrategy.cs | 391 ++-- .../ShapeAttributeOccurrence.cs | 23 +- .../IPlacementNodeFilterProvider.cs | 11 +- .../PathPlacementNodeFilterProvider.cs | 91 +- .../ShapePlacementStrategy/PlacementFile.cs | 41 +- .../ShapePlacementParsingStrategy.cs | 185 +- .../Descriptors/ShapeTable.cs | 19 +- .../Descriptors/ShapeTableBuilder.cs | 41 +- .../ShapeTablePlacementProvider.cs | 75 +- .../BasicShapeTemplateHarvester.cs | 89 +- .../ShapeTemplateStrategy/HarvestShapeHit.cs | 11 +- .../ShapeTemplateStrategy/HarvestShapeInfo.cs | 15 +- .../IShapeTemplateFileProviderAccessor.cs | 17 +- .../IShapeTemplateHarvester.cs | 19 +- .../IShapeTemplateViewEngine.cs | 11 +- .../ShapeTemplateBindingStrategy.cs | 231 ++- .../ShapeTemplateFileProviderAccessor.cs | 53 +- .../ShapeTemplateOptions.cs | 27 +- .../ShapeTemplateOptionsSetup.cs | 51 +- .../DisplayManager.cs | 209 +- .../Events/ThemeFeatureBuilderEvents.cs | 33 +- .../Extensions/ExtensionInfoExtensions.cs | 17 +- .../Extensions/ServiceCollectionExtensions.cs | 35 +- .../ThemeExtensionDependencyStrategy.cs | 19 +- .../Extensions/ThemeExtensionInfo.cs | 59 +- .../FileProviders/ContentFileInfo.cs | 49 +- .../FileProviders/DirectoryContents.cs | 41 +- .../Handlers/BuildDisplayContext.cs | 17 +- .../Handlers/BuildEditorContext.cs | 19 +- .../Handlers/BuildShapeContext.cs | 51 +- .../Handlers/DisplayDriver.cs | 249 ++- .../Handlers/DisplayDriverBase.cs | 267 ++- .../Handlers/FindPlacementDelegate.cs | 31 +- .../Handlers/IBuildShapeContext.cs | 27 +- .../Handlers/IDisplayDriver.cs | 25 +- .../Handlers/IDisplayHandler.cs | 13 +- .../Handlers/UpdateEditorContext.cs | 17 +- .../Html/HtmlContentString.cs | 41 +- .../IAsyncViewActionFilter.cs | 9 +- .../IDisplayHelper.cs | 9 +- .../IDisplayManager.cs | 13 +- .../INamedEnumerable.cs | 11 +- .../IPositioned.cs | 9 +- .../OrchardCore.DisplayManagement/IShape.cs | 169 +- .../IShapeBindingResolver.cs | 19 +- .../IShapeFactory.cs | 227 ++- .../Implementation/DefaultHtmlDisplay.cs | 439 +++-- .../Implementation/DefaultShapeFactory.cs | 205 +- .../Implementation/DisplayContext.cs | 33 +- .../Implementation/DisplayHelper.cs | 151 +- .../Implementation/IHtmlDisplay.cs | 15 +- .../Implementation/IShapeDisplayEvents.cs | 19 +- .../Implementation/IShapeFactoryEvents.cs | 63 +- .../Implementation/ShapeDisplayContext.cs | 15 +- .../Layout/ILayoutAccessor.cs | 9 +- .../Layout/LayoutAccessor.cs | 41 +- .../ThemeViewLocationExpanderProvider.cs | 127 +- .../Manifest/ThemeAttribute.cs | 393 ++-- .../ModelBinding/ControllerModelUpdater.cs | 69 +- .../ModelBinding/LocalModelBinderAccessor.cs | 31 +- .../ModelBinding/ModelBinderAccessorFilter.cs | 51 +- .../ModelBinding/ModelStateWrapperUpdater.cs | 99 +- .../ModelBinding/NullModelUpdater.cs | 23 +- .../ModelBinding/PageModelUpdater.cs | 21 +- .../ModelBinding/PrefixedModelUpdater.cs | 47 +- .../Notify/INotifier.cs | 41 +- .../Notify/Notifier.cs | 35 +- .../Notify/NotifierExtensions.cs | 71 +- .../Notify/NotifyEntry.cs | 71 +- .../Notify/NotifyEntryComparer.cs | 31 +- .../Notify/NotifyEntryConverter.cs | 73 +- .../Notify/NotifyFilter.cs | 307 ++- .../ObjectPool.cs | 379 ++-- .../OrchardCoreBuilderExtensions.cs | 187 +- .../OrchardDisplayHelper.cs | 19 +- .../PositionWrapper.cs | 105 +- .../Razor/IRazorViewExtensionProvider.cs | 9 +- .../Razor/RazorPage.cs | 493 +++-- .../Razor/RazorShapeTemplateViewEngine.cs | 249 ++- .../Razor/RazorViewActionFilter.cs | 109 +- .../Razor/RazorViewFeature.cs | 33 +- .../RazorPages/Page.cs | 415 ++-- .../ShapeAttribute.cs | 23 +- .../Shapes/AlternatesCollection.cs | 169 +- .../Shapes/Composite.cs | 335 ++-- .../Shapes/CoreShapes.cs | 181 +- .../Shapes/DateTimeShapes.cs | 227 ++- .../Shapes/GroupShapes.cs | 315 ++- .../Shapes/PageTitleShapes.cs | 43 +- .../Shapes/Shape.cs | 335 ++-- .../Shapes/ShapeDebugView.cs | 75 +- .../Shapes/ShapeMetadata.cs | 131 +- .../Shapes/ShapeSerializer.cs | 147 +- .../TagHelpers/AddAlternateTagHelper.cs | 19 +- .../TagHelpers/AddClassTagHelper.cs | 19 +- .../TagHelpers/AddPropertyTagHelper.cs | 6 +- .../TagHelpers/AddWrapperTagHelper.cs | 19 +- .../TagHelpers/BaseShapeTagHelper.cs | 259 ++- .../TagHelpers/ClearAlternatesTagHelper.cs | 19 +- .../TagHelpers/ClearClassesTagHelper.cs | 19 +- .../TagHelpers/ClearWrappersTagHelper.cs | 19 +- .../TagHelpers/DateTimeTagHelper.cs | 53 +- .../TagHelpers/InputIsDisabledTagHelper.cs | 27 +- .../TagHelpers/RemoveAlternateTagHelper.cs | 19 +- .../TagHelpers/RemoveClassTagHelper.cs | 19 +- .../TagHelpers/RemoveWrapperTagHelper.cs | 19 +- .../TagHelpers/ShapeMetadataTagHelper.cs | 27 +- .../TagHelpers/ShapeTagHelper.cs | 15 +- .../TagHelpers/TimeSpanTagHelper.cs | 53 +- .../TagHelpers/ValidationMessageTagHelper.cs | 113 +- .../TagHelpers/ZoneTagHelper.cs | 79 +- .../Theming/IThemeManager.cs | 9 +- .../Theming/IThemeSelector.cs | 25 +- .../Theming/ThemeLayout.cs | 89 +- .../Theming/ThemeManager.cs | 81 +- .../Theming/ThemeSelectorResult.cs | 27 +- .../Theming/ThemeViewStart.cs | 33 +- .../Theming/ThemingViewsFeatureProvider.cs | 45 +- .../Title/IPageTitleBuilder.cs | 67 +- .../Title/PageTitleBuilder.cs | 125 +- .../ViewContextAccessor.cs | 9 +- .../ViewModels/GroupViewModel.cs | 25 +- .../Views/CombinedResult.cs | 51 +- .../Views/IDisplayResult.cs | 11 +- .../Views/ShapeResult.cs | 449 +++-- .../Views/ShapeViewModel.cs | 107 +- .../Views/ShapeViewModelOfT.cs | 25 +- .../Zones/FlatPositionComparer.cs | 147 +- .../Zones/ZoneOnDemand.cs | 187 +- .../Zones/ZoneShapes.cs | 615 +++--- .../DynamicCacheOptions.cs | 9 +- .../IDynamicCache.cs | 13 +- .../IDynamicCacheService.cs | 11 +- .../MailMessage.cs | 97 +- .../MailMessageAttachment.cs | 25 +- .../MailMessageRecipients.cs | 2 +- .../SmtpDeliveryMethod.cs | 17 +- .../SmtpEncryptionMethod.cs | 19 +- .../Models/ModuleFeature.cs | 99 +- .../Extensions/FeedBuilderExtensions.cs | 11 +- .../IFeedBuilder.cs | 13 +- .../IFeedBuilderProvider.cs | 19 +- .../IFeedItemBuilder.cs | 9 +- .../IFeedQuery.cs | 9 +- .../IFeedQueryProvider.cs | 19 +- .../Models/ContextualizeContext.cs | 11 +- .../Models/FeedContext.cs | 25 +- .../Models/FeedItem.cs | 37 +- .../Models/FeedMetadata.cs | 17 +- .../Models/FeedResponse.cs | 17 +- .../Rss/RssFeedBuilder.cs | 61 +- .../Rss/RssFeedBuilderProvider.cs | 23 +- .../ServiceCollectionExtensions.cs | 13 +- .../FileStoreException.cs | 15 +- .../IFileStore.cs | 321 ++- .../IFileStoreCache.cs | 17 +- .../IFileStoreEntry.cs | 57 +- .../BlobDirectory.cs | 43 +- .../BlobFile.cs | 59 +- .../BlobFileStore.cs | 669 ++++--- .../BlobStorageOptions.cs | 43 +- .../FileSystemStore.cs | 531 +++-- .../FileSystemStoreEntry.cs | 37 +- .../BuildFieldIndexContext.cs | 35 +- .../BuildIndexContext.cs | 37 +- .../BuildPartIndexContext.cs | 27 +- .../ContentFieldIndexHandler.cs | 75 +- .../ContentPartIndexHandler.cs | 51 +- .../DocumentIndexOptions.cs | 33 +- .../IContentFieldIndexHandler.cs | 17 +- .../IContentIndexSettings.cs | 17 +- .../IContentItemIndexHandler.cs | 15 +- .../IContentPartIndexHandler.cs | 17 +- .../IIndexingTaskManager.cs | 27 +- .../IndexingTask.cs | 49 +- .../StorableAttribute.cs | 17 +- .../Cache/CacheContext.cs | 149 +- .../Cache/CacheContextEntry.cs | 19 +- .../Cache/CacheOptions.cs | 11 +- .../Cache/ICacheContextManager.cs | 17 +- .../Cache/ICacheContextProvider.cs | 19 +- .../Cache/ICacheScopeManager.cs | 37 +- .../Cache/ITagCache.cs | 21 +- .../Commands/CommandContext.cs | 19 +- .../Commands/CommandDescriptor.cs | 13 +- .../Commands/CommandHandlerDescriptor.cs | 9 +- .../Commands/CommandNameAttribute.cs | 61 +- .../Commands/CommandParameters.cs | 15 +- .../Commands/CommandReturnCodes.cs | 19 +- .../Commands/DefaultCommandHandler.cs | 271 ++- .../Commands/ICommandHandler.cs | 9 +- .../Commands/ICommandManager.cs | 11 +- .../Commands/OrchardSwitchAttribute.cs | 9 +- .../Commands/OrchardSwitchesAttribute.cs | 27 +- .../Parameters/ICommandParametersParser.cs | 9 +- .../Commands/Parameters/ICommandParser.cs | 217 ++- .../Documents/IDocumentEntity.cs | 13 +- .../Documents/IDocumentEntityManager.cs | 33 +- .../Documents/IDocumentEntityManagerOfT.cs | 15 +- .../Documents/IDocumentManager.cs | 33 +- .../Documents/IDocumentManagerOfT.cs | 15 +- .../Documents/IDocumentSerializer.cs | 25 +- .../IVolatileDocumentEntityManager.cs | 13 +- .../Documents/IVolatileDocumentManager.cs | 19 +- .../Documents/Options/DocumentNamedOptions.cs | 11 +- .../Documents/Options/DocumentOptions.cs | 17 +- .../Documents/Options/DocumentOptionsBase.cs | 25 +- .../Options/DocumentSharedOptions.cs | 9 +- .../Email/EmailAddressAttribute.cs | 31 +- .../Email/IEmailAddressValidator.cs | 21 +- .../Entities/IIdGenerator.cs | 9 +- .../Files/FileProviderExtensions.cs | 17 +- .../Files/PathExtensions.cs | 195 +- .../Html/IHtmlSanitizerService.cs | 9 +- .../Scripting/GlobalMethod.cs | 11 +- .../Scripting/IGlobalMethodProvider.cs | 21 +- .../Scripting/IScriptingEngine.cs | 13 +- .../Scripting/IScriptingManager.cs | 51 +- .../Scripting/IScriptingScope.cs | 7 +- .../Security/ApiAuthorizationOptions.cs | 9 +- .../AuthorizationServiceExtensions.cs | 25 +- .../Security/IPermissionGrantingService.cs | 21 +- .../Security/IRole.cs | 11 +- .../Security/IRoleCreatedEventHandler.cs | 9 +- .../Security/IRoleRemovedEventHandler.cs | 9 +- .../Security/PermissionRequirement.cs | 17 +- .../Permissions/IPermissionProvider.cs | 29 +- .../Security/Permissions/Permission.cs | 63 +- .../Security/Role.cs | 39 +- .../Security/RoleClaim.cs | 39 +- .../Security/SecurityHeaderNames.cs | 15 +- .../Security/Services/IRoleService.cs | 13 +- .../Services/RoleServiceExtensions.cs | 55 +- .../Settings/CacheMode.cs | 15 +- .../Settings/ISiteService.cs | 33 +- .../Cache/CacheContextManager.cs | 31 +- .../FeaturesCacheContextProvider.cs | 43 +- .../KnownValueCacheContextProvider.cs | 19 +- .../QueryCacheContextProvider.cs | 67 +- .../RolesCacheContextProvider.cs | 41 +- .../RouteCacheContextProvider.cs | 31 +- .../UserCacheContextProvider.cs | 41 +- .../Cache/CacheScopeManager.cs | 191 +- .../Cache/DefaultTagCache.cs | 97 +- .../Cache/OrchardCoreBuilderExtensions.cs | 81 +- .../Commands/Builtin/HelpCommand.cs | 97 +- .../CommandHandlerDescriptorBuilder.cs | 77 +- .../Commands/DefaultCommandManager.cs | 141 +- .../Commands/OrchardCoreBuilderExtensions.cs | 27 +- .../Parameters/CommandParametersParser.cs | 55 +- .../Documents/DefaultDocumentSerializer.cs | 131 +- .../Documents/DocumentEntityManager.cs | 59 +- .../Documents/DocumentEntityManagerOfT.cs | 19 +- .../Documents/DocumentManager.cs | 557 +++--- .../Documents/DocumentManagerOfT.cs | 29 +- .../Documents/Options/DocumentOptionsSetup.cs | 125 +- .../Documents/OrchardCoreBuilderExtensions.cs | 33 +- .../VolatileDocumentEntityManager.cs | 17 +- .../Documents/VolatileDocumentManager.cs | 167 +- .../Email/EmailAddressValidator.cs | 19 +- .../Email/OrchardCoreBuilderExtensions.cs | 27 +- .../Entities/DefaultIdGenerator.cs | 99 +- .../Entities/OrchardCoreBuilderExtensions.cs | 27 +- .../Entities/Scripting/IdGeneratorMethod.cs | 29 +- .../Entities/ServiceCollectionExtensions.cs | 15 +- .../Html/HtmlSanitizerOptions.cs | 9 +- .../Html/HtmlSanitizerOptionsExtensions.cs | 21 +- .../Html/HtmlSanitizerService.cs | 21 +- .../Html/OrchardCoreBuilderExtensions.cs | 37 +- .../Scripting/CommonGeneratorMethods.cs | 77 +- .../Scripting/DefaultScriptingManager.cs | 69 +- .../Scripting/Files/FilesScriptEngine.cs | 83 +- .../Scripting/Files/FilesScriptScope.cs | 19 +- .../Scripting/OrchardCoreBuilderExtensions.cs | 33 +- .../Scripting/ServiceCollectionExtensions.cs | 17 +- .../Security/ApiAuthenticationHandler.cs | 93 +- .../PermissionHandler.cs | 39 +- .../DefaultPermissionGrantingService.cs | 65 +- .../Security/OrchardCoreBuilderExtensions.cs | 39 +- .../Shell/ShellDescriptorManager.cs | 205 +- .../ShellOrchardCoreBuilderExtensions.cs | 55 +- .../DatabaseShellConfigurationSources.cs | 211 +- .../DatabaseShellsSettingsSources.cs | 203 +- .../DatabaseShellsStorageOptions.cs | 17 +- .../DatabaseShellContextFactoryExtensions.cs | 35 +- ...abaseShellsOrchardCoreBuilderExtensions.cs | 23 +- .../Models/DatabaseShellConfigurations.cs | 9 +- .../Models/DatabaseShellsSettings.cs | 9 +- .../ILiquidFilter.cs | 9 +- .../ILiquidTemplateManager.cs | 83 +- .../LiquidContentAccessor.cs | 13 +- .../LiquidFilterDelegateResolver.cs | 19 +- .../LiquidPropertyAccessor.cs | 39 +- .../LiquidTemplateContext.cs | 25 +- .../ServiceCollectionExtensions.cs | 15 +- .../CultureDictionary.cs | 135 +- .../CultureDictionaryRecord.cs | 96 +- .../CultureDictionaryRecordKey.cs | 37 +- .../Extensions/HtmlLocalizerExtensions.cs | 73 +- .../Extensions/StringLocalizerExtensions.cs | 71 +- .../Extensions/ViewLocalizerExtensions.cs | 73 +- .../ILocalizationFileLocationProvider.cs | 21 +- .../ILocalizationManager.cs | 21 +- .../IPluralRuleProvider.cs | 35 +- .../IPluralStringLocalizer.cs | 23 +- .../ITranslationProvider.cs | 21 +- .../PluralForm.cs | 73 +- .../PluralFormNotFoundException.cs | 87 +- .../PluralizationArgument.cs | 33 +- .../PluralizationRuleDelegate.cs | 17 +- .../DataAnnotationsDefaultErrorMessages.cs | 119 +- .../LocalizedDataAnnotationsMvcOptions.cs | 25 +- .../LocalizedValidationMetadataProvider.cs | 75 +- .../DefaultPluralRuleProvider.cs | 113 +- ...LocalizationServiceCollectionExtensions.cs | 81 +- .../LocalizationManager.cs | 87 +- .../ContentRootPoFileLocationProvider.cs | 41 +- .../PoFilesTranslationsProvider.cs | 55 +- .../PortableObject/PoParser.cs | 323 ++- .../PortableObjectHtmlLocalizer.cs | 69 +- .../PortableObjectHtmlLocalizerFactory.cs | 37 +- .../PortableObjectStringLocalizer.cs | 331 ++-- .../PortableObjectStringLocalizerFactory.cs | 113 +- .../TenantLayoutRenderer.cs | 29 +- .../ApplicationBuilderExtensions.cs | 11 +- .../SerilogTenantNameLoggingMiddleware.cs | 27 +- .../WebHostBuilderExtensions.cs | 17 +- .../Services/IMarkdownService.cs | 9 +- .../Events/IMediaCreatingEventHandler.cs | 21 +- .../Events/IMediaEventHandler.cs | 29 +- .../Events/MediaContextBase.cs | 15 +- .../Events/MediaCreatedContext.cs | 9 +- .../Events/MediaCreatingContext.cs | 7 +- .../Events/MediaDeletedContext.cs | 9 +- .../Events/MediaDeletingContext.cs | 7 +- .../Events/MediaMoveContext.cs | 11 +- .../IMediaFileProvider.cs | 7 +- .../IMediaFileStore.cs | 21 +- .../IMediaFileStoreCache.cs | 19 +- .../IMediaFileStoreCacheFileProvider.cs | 13 +- .../IMediaNameNormalizerService.cs | 11 +- .../IMediaTokenService.cs | 19 +- .../IUserAssetFolderNameProvider.cs | 9 +- .../Indexing/IMediaFileTextProvider.cs | 23 +- .../Indexing/MediaFileIndexingOptions.cs | 57 +- .../Indexing/ServiceCollectionExtensions.cs | 43 +- .../MediaOptions.cs | 187 +- .../DefaultMediaFileStore.cs | 309 ++- .../DefaultMediaFileStoreCacheFileProvider.cs | 225 ++- .../DefaultMediaFileStoreCacheEventHandler.cs | 45 +- .../Events/MediaEventHandlerBase.cs | 69 +- .../Fields/MediaField.cs | 11 +- .../Services/MediaSizeLimitAttribute.cs | 83 +- .../Settings/MediaFieldSettings.cs | 23 +- .../IMetaWeblogDriver.cs | 13 +- .../MetaWeblogDriver.cs | 21 +- .../ApplicationViewFileProvider.cs | 157 +- .../DevelopmentViewsFeature.cs | 7 +- .../Extensions/ControllerBaseExtensions.cs | 113 +- .../ModelStateDictionaryExtensions.cs | 33 +- .../OrchardCoreBuilderExtensions.cs | 41 +- .../FileProviders/FileProviderExtensions.cs | 67 +- .../ComponentViewLocationExpanderProvider.cs | 189 +- .../CompositeViewLocationExpanderProvider.cs | 57 +- .../IViewLocationExpanderProvider.cs | 9 +- .../SharedViewLocationExpanderProvider.cs | 199 +- .../ModelBinding/CheckMarkModelBinder.cs | 43 +- .../CheckMarkModelBinderProvider.cs | 29 +- .../ModelBinding/ModelError.cs | 19 +- .../ModularApplicationModelProvider.cs | 95 +- .../ModularRazorViewEngineOptionsSetup.cs | 17 +- .../ModuleProjectRazorFileProvider.cs | 343 ++-- .../RazorCompilationFileProviderAccessor.cs | 49 +- .../RazorCompilationOptionsSetup.cs | 53 +- .../ModularPageApplicationModelProvider.cs | 63 +- .../ModularPageMvcCoreBuilderExtensions.cs | 33 +- .../ModularPageRazorPagesOptionsSetup.cs | 45 +- .../ModularPageViewEnginePathFilter.cs | 37 +- .../PageConventionCollectionExtensions.cs | 117 +- .../RazorPages/PageEndpointComparerPolicy.cs | 19 +- .../DefaultAreaControllerRouteMapper.cs | 29 +- .../SharedViewCompilerProvider.cs | 49 +- .../ShellFeatureApplicationPart.cs | 109 +- .../ShellFileVersionProvider.cs | 187 +- .../ShellViewFeatureProvider.cs | 239 ++- .../OrchardCore.Mvc.Core/Startup.cs | 169 +- .../Utilities/ControllerTypeExtensions.cs | 21 +- .../Utilities/StringExtensions.cs | 737 ++++--- .../Utilities/UrlHelperExtensions.cs | 37 +- .../INavigationManager.cs | 9 +- .../INavigationProvider.cs | 9 +- .../OrchardCore.Navigation.Core/MenuItem.cs | 183 +- .../NavigationBuilder.cs | 165 +- .../NavigationExtensions.cs | 25 +- .../NavigationItemBuilder.cs | 265 ++- .../NavigationManager.cs | 383 ++-- .../OrchardCore.Navigation.Core/Pager.cs | 87 +- .../PagerParameters.cs | 25 +- .../OrchardCore.Navigation.Core/PagerSlim.cs | 71 +- .../PagerSlimParameters.cs | 27 +- .../ServiceCollectionExtensions.cs | 23 +- .../Services/EmailNotificationProvider.cs | 2 +- .../OpenIdApplicationDescriptor.cs | 15 +- .../OpenIdAuthorizationDescriptor.cs | 7 +- .../Descriptors/OpenIdScopeDescriptor.cs | 7 +- .../Descriptors/OpenIdTokenDescriptor.cs | 7 +- .../Managers/IOpenIdApplicationManager.cs | 33 +- .../Managers/IOpenIdAuthorizationManager.cs | 27 +- .../Managers/IOpenIdScopeManager.cs | 27 +- .../Managers/IOpenIdTokenManager.cs | 27 +- .../Stores/IOpenIdApplicationStore.cs | 17 +- .../Stores/IOpenIdAuthorizationStore.cs | 11 +- .../Abstractions/Stores/IOpenIdScopeStore.cs | 11 +- .../Abstractions/Stores/IOpenIdTokenStore.cs | 11 +- .../OpenIdConstants.cs | 53 +- .../OpenIdExtensions.cs | 127 +- .../Managers/OpenIdApplicationManager.cs | 317 ++- .../Managers/OpenIdAuthorizationManager.cs | 99 +- .../Services/Managers/OpenIdScopeManager.cs | 97 +- .../Services/Managers/OpenIdTokenManager.cs | 97 +- .../YesSql/Indexes/OpenIdApplicationIndex.cs | 171 +- .../Indexes/OpenIdAuthorizationIndex.cs | 55 +- .../YesSql/Indexes/OpenIdScopeIndex.cs | 83 +- .../YesSql/Indexes/OpenIdTokenIndex.cs | 67 +- .../YesSql/Migrations/OpenIdMigrations.cs | 947 +++++---- .../YesSql/Models/OpenIdApplication.cs | 195 +- .../YesSql/Models/OpenIdAuthorization.cs | 107 +- .../YesSql/Models/OpenIdScope.cs | 109 +- .../YesSql/Models/OpenIdToken.cs | 131 +- .../OpenIdApplicationStoreResolver.cs | 69 +- .../OpenIdAuthorizationStoreResolver.cs | 69 +- .../Resolvers/OpenIdScopeStoreResolver.cs | 69 +- .../Resolvers/OpenIdTokenStoreResolver.cs | 69 +- .../YesSql/Stores/OpenIdApplicationStore.cs | 709 ++++--- .../YesSql/Stores/OpenIdAuthorizationStore.cs | 655 ++++--- .../YesSql/Stores/OpenIdScopeStore.cs | 475 +++-- .../YesSql/Stores/OpenIdTokenStore.cs | 793 ++++---- .../IQueryResults.cs | 17 +- .../Models/DeletingQueryContext.cs | 2 +- .../Models/UpdatedQueryContext.cs | 2 +- .../ActionFilters/Detection/IDetectRobots.cs | 43 +- .../Detection/IPAddressRobotDetector.cs | 85 +- .../ActionFilters/ReCaptchaMode.cs | 23 +- .../ValidateReCaptchaAttribute.cs | 87 +- .../Configuration/ReCaptchaSettings.cs | 33 +- .../ReCaptchaSettingsConfiguration.cs | 31 +- .../OrchardCore.ReCaptcha.Core/Constants.cs | 13 +- .../ServiceCollectionExtensions.cs | 55 +- .../Services/ReCaptchaService.cs | 221 ++- .../TagHelpers/ReCaptchaTagHelper.cs | 191 +- .../Events/IRecipeEventHandler.cs | 27 +- .../Models/RecipeDescriptor.cs | 41 +- .../Models/RecipeEnvironmentFeature.cs | 9 +- .../Models/RecipeResult.cs | 15 +- .../ServiceCollectionExtensions.cs | 17 +- .../Services/IRecipeEnvironmentProvider.cs | 11 +- .../Services/IRecipeExecutor.cs | 9 +- .../Services/IRecipeHarvester.cs | 15 +- .../Services/IRecipeMigrator.cs | 9 +- .../Services/IRecipeReader.cs | 9 +- .../Services/IRecipeStepHandler.cs | 21 +- .../ConfigurationMethodProvider.cs | 29 +- .../ParametersMethodProvider.cs | 37 +- .../ServiceCollectionExtensions.cs | 23 +- .../Services/ApplicationRecipeHarvester.cs | 33 +- .../RecipeEnvironmentFeatureProvider.cs | 55 +- .../Services/RecipeExecutor.cs | 359 ++-- .../Services/RecipeHarvester.cs | 89 +- .../Services/RecipeMigrator.cs | 83 +- .../Services/RecipeReader.cs | 51 +- .../VariablesMethodProvider.cs | 67 +- .../IRedisService.cs | 15 +- .../RedisOptions.cs | 33 +- .../IResourceManager.cs | 249 ++- .../LinkEntry.cs | 125 +- .../MetaEntry.cs | 177 +- .../RequireSettings.cs | 475 +++-- .../ResourceDebugMode.cs | 13 +- .../ResourceDefinition.cs | 573 +++--- .../ResourceLocation.cs | 15 +- .../ResourceManagementOptions.cs | 27 +- .../ResourceManifest.cs | 61 +- .../ResourcePosition.cs | 13 +- .../ResourceRequiredContext.cs | 63 +- .../ResourceDictionary.cs | 61 +- .../ResourceManager.cs | 1031 +++++----- .../ServiceCollectionExtensions.cs | 19 +- .../TagHelpers/LinkTagHelper.cs | 107 +- .../TagHelpers/MetaTagHelper.cs | 65 +- .../TagHelpers/ResourcesTagHelper.cs | 69 +- .../TagHelpers/ScriptTagHelper.cs | 365 ++-- .../TagHelpers/StyleTagHelper.cs | 509 +++-- .../Condition.cs | 11 +- .../ConditionEvaluator.cs | 17 +- .../ConditionGroup.cs | 17 +- .../ConditionOperator.cs | 7 +- .../ConditionOperatorOptions.cs | 79 +- .../ConditionOptions.cs | 31 +- .../IConditionFactory.cs | 31 +- .../IConditionOperatorFactory.cs | 25 +- .../INegateOperator.cs | 7 +- .../IOperatorComparer.cs | 9 +- .../OperatorComparer.cs | 13 +- .../OrchardCore.Rules.Abstractions/Rule.cs | 7 +- .../ServiceCollectionExtensions.cs | 71 +- .../Services/IConditionIdGenerator.cs | 9 +- .../Services/IConditionOperatorResolver.cs | 9 +- .../Services/IConditionResolver.cs | 9 +- .../Services/IRuleService.cs | 9 +- .../JavaScriptEngine.cs | 89 +- .../JavaScriptScope.cs | 19 +- .../ServiceCollectionExtensions.cs | 13 +- .../ViewModels/SearchFormViewModel.cs | 29 +- .../ViewModels/SearchIndexViewModel.cs | 45 +- .../ViewModels/SearchResultsViewModel.cs | 27 +- .../AzureAISearchFieldIndexEventsBase.cs | 4 +- .../ElasticQueryResults.cs | 11 +- .../ElasticTopDocs.cs | 13 +- .../IElasticAnalyzer.cs | 17 +- .../IElasticQueryService.cs | 31 +- .../IElasticsearchQueryService.cs | 17 +- .../ElasticIndexDeploymentSource.cs | 59 +- .../Deployment/ElasticIndexDeploymentStep.cs | 23 +- .../ElasticIndexRebuildDeploymentSource.cs | 39 +- .../ElasticIndexRebuildDeploymentStep.cs | 23 +- .../ElasticIndexResetDeploymentSource.cs | 41 +- .../ElasticIndexResetDeploymentStep.cs | 23 +- .../ElasticSettingsDeploymentSource.cs | 39 +- .../ElasticSettingsDeploymentStep.cs | 17 +- .../Handlers/ElasticIndexingContentHandler.cs | 167 +- .../Mappings/ContainedPartModel.cs | 15 +- .../Mappings/DisplayTextModel.cs | 19 +- ...ContentPickerFieldElasticEditorSettings.cs | 13 +- .../Models/ElasticConnectionOptions.cs | 97 +- .../Models/ElasticContentIndexSettings.cs | 23 +- .../ElasticContentPickerResultProvider.cs | 163 +- .../Recipes/ElasticIndexRebuildStep.cs | 75 +- .../Recipes/ElasticIndexResetStep.cs | 75 +- .../Recipes/ElasticIndexStep.cs | 59 +- .../Recipes/ElasticSettingsStep.cs | 41 +- .../Services/ElasticAnalyzer.cs | 33 +- .../ElasticIndexInitializerService.cs | 125 +- .../Services/ElasticIndexManager.cs | 955 +++++---- .../Services/ElasticIndexSettingsService.cs | 161 +- .../Services/ElasticIndexingService.cs | 469 +++-- .../Services/ElasticQueryService.cs | 121 +- .../Services/ElasticQuerySource.cs | 115 +- .../Services/ElasticSearchQueryService.cs | 35 +- .../ElasticContentPickerShapeProvider.cs | 33 +- .../ILuceneAnalyzer.cs | 17 +- .../ILuceneQueryProvider.cs | 9 +- .../ILuceneQueryService.cs | 25 +- .../ILuceneSearchQueryService.cs | 25 +- .../Indexing/Index.cs | 41 +- .../Indexing/Mapping.cs | 17 +- .../Indexing/Property.cs | 103 +- .../Indexing/Types.cs | 33 +- .../LuceneOptions.cs | 9 +- .../LuceneQueryContext.cs | 23 +- .../LuceneQueryResults.cs | 11 +- .../LuceneTopDocs.cs | 11 +- .../LuceneQueryService.cs | 183 +- .../QueryProviders/BooleanQueryProvider.cs | 169 +- .../Filters/FuzzyFilterProvider.cs | 89 +- .../Filters/GeoBoundingBoxFilterProvider.cs | 75 +- .../Filters/GeoDistanceFilterProvider.cs | 291 ++- .../Filters/ILuceneBooleanFilterProvider.cs | 9 +- .../Filters/MatchAllFilterProvider.cs | 31 +- .../Filters/MatchFilterProvider.cs | 109 +- .../Filters/MatchPhraseFilterProvider.cs | 83 +- .../Filters/PrefixFilterProvider.cs | 89 +- .../Filters/RangeFilterProvider.cs | 177 +- .../Filters/TermFilterProvider.cs | 81 +- .../Filters/TermsFilterProvider.cs | 67 +- .../Filters/WildcardFilterProvider.cs | 93 +- .../QueryProviders/FuzzyQueryProvider.cs | 83 +- .../QueryProviders/MatchAllQueryProvider.cs | 27 +- .../MatchPhraseQueryProvider.cs | 69 +- .../QueryProviders/MatchQueryProvider.cs | 85 +- .../QueryProviders/PrefixQueryProvider.cs | 85 +- .../QueryStringQueryProvider.cs | 23 +- .../QueryProviders/RangeQueryProvider.cs | 157 +- .../QueryProviders/RegexpQueryProvider.cs | 53 +- .../SimpleQueryStringQueryProvider.cs | 61 +- .../QueryProviders/TermQueryProvider.cs | 47 +- .../QueryProviders/TermsQueryProvider.cs | 51 +- .../QueryProviders/WildcardQueryProvider.cs | 51 +- .../ServiceCollectionExtensions.cs | 67 +- .../Deployment/ServiceCollectionExtensions.cs | 31 +- .../SiteSettingsPropertyDeploymentSource.cs | 51 +- .../SiteSettingsPropertyDeploymentStep.cs | 17 +- ...iteSettingsPropertyDeploymentStepDriver.cs | 57 +- ...teSettingsPropertyDeploymentStepFactory.cs | 19 +- ...SettingsPropertyDeploymentStepViewModel.cs | 11 +- .../ISetupService.cs | 31 +- .../ISetupUserIdGenerator.cs | 9 +- .../SetupContext.cs | 49 +- .../ServiceCollectionExtensions.cs | 23 +- .../OrchardCore.Setup.Core/SetupService.cs | 409 ++-- .../SetupUserIdGenerator.cs | 23 +- .../BlobShellConfigurationSources.cs | 149 +- .../Configuration/BlobShellStorageOptions.cs | 9 +- .../BlobShellsConfigurationSources.cs | 115 +- .../BlobShellsSettingsSources.cs | 157 +- .../BlobShellsOrchardCoreBuilderExtensions.cs | 93 +- .../Services/BlobShellsFileStore.cs | 43 +- .../Services/IShellsFileStore.cs | 15 +- .../ServiceCollectionExtensions.cs | 35 +- .../Services/IShortcodeContextProvider.cs | 15 +- .../Services/IShortcodeDescriptorManager.cs | 9 +- .../Services/IShortcodeDescriptorProvider.cs | 9 +- .../Services/IShortcodeService.cs | 9 +- .../ShortcodeDescriptor.cs | 71 +- .../ShortcodeOption.cs | 71 +- .../ShortcodeOptions.cs | 49 +- .../Aspects/SitemapMetadataAspect.cs | 13 +- .../Builders/ContentItemsQueryContext.cs | 23 +- .../Builders/IContentItemsQueryProvider.cs | 15 +- .../Builders/ISitemapBuilder.cs | 15 +- ...emapContentItemExtendedMetadataProvider.cs | 25 +- .../Builders/ISitemapModifiedDateProvider.cs | 15 +- .../Builders/ISitemapSourceBuilder.cs | 15 +- .../ISitemapSourceModifiedDateProvider.cs | 15 +- .../Builders/ISitemapTypeBuilder.cs | 15 +- .../Builders/SitemapBuilderContext.cs | 21 +- .../Builders/SitemapSourceBuilderBase.cs | 25 +- .../SitemapSourceModifiedDateProviderBase.cs | 39 +- .../Builders/SitemapTypeBuilderBase.cs | 25 +- .../Cache/ISitemapCacheFileResolver.cs | 15 +- .../Cache/ISitemapCacheProvider.cs | 27 +- .../Handlers/ISitemapSourceUpdateHandler.cs | 15 +- .../Handlers/ISitemapTypeUpdateHandler.cs | 15 +- .../Handlers/ISitemapUpdateHandler.cs | 15 +- .../Handlers/SitemapUpdateContext.cs | 9 +- .../Models/ChangeFrequency.cs | 21 +- .../Models/ContentTypesSitemapSource.cs | 89 +- .../Models/CustomPathSitemapSource.cs | 43 +- .../Models/Sitemap.cs | 9 +- .../Models/SitemapSource.cs | 9 +- .../Models/SitemapType.cs | 77 +- .../Services/ISitemapHelperService.cs | 17 +- .../Services/ISitemapIdGenerator.cs | 9 +- .../Services/ISitemapManager.cs | 91 +- .../Services/ISitemapSourceFactory.cs | 41 +- .../SitemapsOptions.cs | 11 +- .../SitemapsRazorPagesContentTypeOption.cs | 39 +- .../SitemapsRazorPagesOptions.cs | 25 +- .../ClaimsPrincipalExtensions.cs | 39 +- .../Events/ILoginFormEvent.cs | 61 +- .../Events/IPasswordRecoveryFormEvents.cs | 45 +- .../Events/IRegistrationFormEvents.cs | 29 +- .../Events/PasswordRecoveryContext.cs | 23 +- .../Handlers/IExternalLoginEventHandler.cs | 35 +- .../Handlers/IUserEventHandler.cs | 105 +- .../Handlers/UpdateUserContext.cs | 113 +- .../Handlers/UserContext.cs | 17 +- .../Handlers/UserContextBase.cs | 31 +- .../Handlers/UserCreateContext.cs | 21 +- .../Handlers/UserDeleteContext.cs | 21 +- .../Handlers/UserEventHandlerBase.cs | 39 +- .../Handlers/UserUpdateContext.cs | 21 +- .../OrchardCore.Users.Abstractions/IUser.cs | 17 +- .../Models/UserClaim.cs | 31 +- .../SerializableClaim.cs | 77 +- .../Services/IMembershipService.cs | 47 +- .../Services/IUserClaimsProvider.cs | 9 +- .../Services/IUserIdGenerator.cs | 9 +- .../Services/IUserService.cs | 153 +- .../UserOptions.cs | 77 +- .../UsersServiceCollectionExtensions.cs | 81 +- .../Handlers/UserDisabledEventHandler.cs | 21 +- .../Indexes/UserByClaimIndex.cs | 31 +- .../Indexes/UserByLoginInfoIndex.cs | 31 +- .../Indexes/UserByRoleNameIndex.cs | 91 +- .../Indexes/UserIndex.cs | 57 +- .../OrchardCore.Users.Core/Models/User.cs | 55 +- .../Models/UserToken.cs | 13 +- ...faultUserClaimsPrincipalProviderFactory.cs | 47 +- .../Services/IUsersAdminListFilterParser.cs | 7 +- .../Services/IUsersAdminListFilterProvider.cs | 9 +- .../Services/IUsersAdminListQueryService.cs | 9 +- .../Services/UserFilterEngineModelBinder.cs | 49 +- .../Services/UserQueryContext.cs | 15 +- .../Services/UserService.cs | 483 +++-- .../Services/UserStore.cs | 1283 ++++++------ .../ViewModels/UserIndexOptions.cs | 97 +- .../Activities/Activity.cs | 283 ++- .../Activities/ActivityExecutionResult.cs | 33 +- .../Activities/EventActivity.cs | 13 +- .../Activities/IActivity.cs | 123 +- .../Activities/IEvent.cs | 7 +- .../Activities/ITask.cs | 7 +- .../Converters/LocalizedStringConverter.cs | 25 +- .../Display/ActivityDisplayDriver.cs | 127 +- .../Helpers/ActivityExtensions.cs | 55 +- .../Helpers/DictionaryExtensions.cs | 63 +- .../Helpers/DistributedLockExtensions.cs | 69 +- .../Helpers/ListExtensions.cs | 23 +- .../Helpers/ServiceCollectionExtensions.cs | 11 +- .../Models/ActivityContext.cs | 11 +- .../Models/ActivityMetadata.cs | 17 +- .../Models/ActivityRecord.cs | 41 +- .../Models/BlockingActivity.cs | 27 +- .../Models/ExecutedActivity.cs | 11 +- .../Models/Outcome.cs | 27 +- .../Models/Transition.cs | 37 +- .../Models/Workflow.cs | 97 +- .../Models/WorkflowContext.cs | 15 +- .../Models/WorkflowCreatedContext.cs | 9 +- .../Models/WorkflowDeletedContext.cs | 9 +- .../Models/WorkflowExecutionContext.cs | 195 +- .../WorkflowExecutionExpressionContext.cs | 15 +- .../WorkflowExecutionHandlerContextBase.cs | 15 +- .../Models/WorkflowExecutionScriptContext.cs | 13 +- .../Models/WorkflowExpression.cs | 27 +- .../Models/WorkflowFaultModel.cs | 27 +- .../Models/WorkflowState.cs | 59 +- .../Models/WorkflowStatus.cs | 23 +- .../Models/WorkflowType.cs | 103 +- .../Models/WorkflowTypeContext.cs | 15 +- .../Models/WorkflowTypeCreatedContext.cs | 9 +- .../Models/WorkflowTypeDeletedContext.cs | 9 +- .../Models/WorkflowTypeUpdatedContext.cs | 9 +- .../Models/WorkflowUpdatedContext.cs | 9 +- .../Options/ActivityRegistration.cs | 27 +- .../Options/WorkflowOptions.cs | 105 +- .../Services/IActivityDisplayManager.cs | 7 +- .../Services/IActivityIdGenerator.cs | 9 +- .../Services/IActivityLibrary.cs | 89 +- .../Services/ISecurityTokenService.cs | 23 +- .../IWorkflowExecutionContextHandler.cs | 15 +- .../Services/IWorkflowExpressionEvaluator.cs | 9 +- .../Services/IWorkflowFaultHandler.cs | 17 +- .../Services/IWorkflowHandler.cs | 13 +- .../Services/IWorkflowIdGenerator.cs | 9 +- .../Services/IWorkflowManager.cs | 135 +- .../Services/IWorkflowScriptEvaluator.cs | 9 +- .../Services/IWorkflowStore.cs | 33 +- .../Services/IWorkflowTypeEventHandler.cs | 13 +- .../Services/IWorkflowTypeIdGenerator.cs | 9 +- .../Services/IWorkflowTypeStore.cs | 21 +- .../Services/IWorkflowValueSerializer.cs | 21 +- .../Services/SerializeWorkflowValueContext.cs | 19 +- .../WorkflowExecutionContextHandlerBase.cs | 35 +- .../Services/WorkflowHandlerBase.cs | 27 +- .../Services/WorkflowTypeHandlerBase.cs | 27 +- .../ViewModels/ActivityViewModel.cs | 23 +- .../IXmlRpcDriver.cs | 9 +- .../IXmlRpcHandler.cs | 11 +- .../Models/XRpcArray.cs | 31 +- .../Models/XRpcData.cs | 93 +- .../Models/XRpcMethodCall.cs | 11 +- .../Models/XRpcMethodResponse.cs | 25 +- .../Models/XRpcStruct.cs | 45 +- .../Services/IXmlRpcReader.cs | 57 +- .../Services/IXmlRpcWriter.cs | 57 +- .../XmlRpcContext.cs | 19 +- .../BackgroundTaskScheduler.cs | 83 +- .../Caching/Distributed/DistributedSignal.cs | 31 +- src/OrchardCore/OrchardCore/Caching/Signal.cs | 81 +- .../Extensions/ExtensionDependencyStrategy.cs | 11 +- .../OrchardCore/Extensions/ExtensionInfo.cs | 49 +- .../Extensions/ExtensionManager.cs | 527 +++-- .../Extensions/ExtensionPriorityStrategy.cs | 11 +- .../Extensions/Features/FeatureHash.cs | 73 +- .../Extensions/Features/FeatureInfo.cs | 83 +- .../Extensions/Features/FeaturesProvider.cs | 189 +- .../Features/TypeFeatureProvider.cs | 75 +- .../Extensions/Manifests/ManifestInfo.cs | 31 +- .../Extensions/ServiceCollectionExtensions.cs | 31 +- .../OrchardCore/Localization/BclCalendars.cs | 175 +- .../Localization/DefaultCalendarManager.cs | 75 +- .../Localization/DefaultCalendarSelector.cs | 25 +- .../DefaultLocalizationService.cs | 25 +- .../OrchardCore/Locking/LocalLock.cs | 229 ++- .../Modules/DefaultOrchardHelper.cs | 15 +- .../ApplicationBuilderExtensions.cs | 45 +- .../OrchardCoreBuilderExtensions.cs | 173 +- .../PoweredByOrchardCoreExtensions.cs | 59 +- .../Extensions/RunningShellTableExtensions.cs | 29 +- .../Extensions/ServiceCollectionExtensions.cs | 879 +++++---- .../OrchardCore/Modules/Model/TimeZone.cs | 35 +- .../Modules/ModularBackgroundService.cs | 619 +++--- .../ModularTenantContainerMiddleware.cs | 103 +- .../Modules/ModularTenantRouterMiddleware.cs | 69 +- .../HttpClient/ActiveHandlerTrackingEntry.cs | 159 +- .../HttpClient/ExpiredHandlerTrackingEntry.cs | 37 +- .../LifetimeTrackingHttpMessageHandler.cs | 25 +- .../Overrides/HttpClient/NonCapturingTimer.cs | 47 +- .../HttpClient/TenantHttpClientFactory.cs | 679 ++++--- .../Modules/PoweredByMiddleware.cs | 65 +- .../OrchardCore/Modules/Services/Clock.cs | 129 +- .../Modules/Services/LocalClock.cs | 127 +- .../Modules/Services/SlugService.cs | 109 +- .../Shell/Builders/CompositionStrategy.cs | 97 +- .../Extensions/ServiceCollectionExtensions.cs | 73 +- .../Extensions/ServiceProviderExtensions.cs | 161 +- .../Builders/FeatureAwareServiceCollection.cs | 151 +- .../Shell/Builders/ShellContainerFactory.cs | 247 ++- .../Shell/Builders/ShellContextFactory.cs | 173 +- .../Builders/ShellContextOptionsSetup.cs | 55 +- .../Shell/Builders/StartupBaseMock.cs | 107 +- .../ShellConfigurationSources.cs | 113 +- .../ShellsConfigurationSources.cs | 33 +- .../Configuration/ShellsSettingsSources.cs | 89 +- ...aultTenantOnlyFeatureValidationProvider.cs | 45 +- .../AllFeaturesShellDescriptorManager.cs | 47 +- ...onfiguredFeaturesShellDescriptorManager.cs | 97 +- .../SetFeaturesShellDescriptorManager.cs | 73 +- .../Shell/Distributed/DistributedContext.cs | 193 +- .../DistributedShellHostedService.cs | 1279 ++++++------ .../Shell/Distributed/ShellIdentifier.cs | 11 +- .../FeatureProfilesValidationProvider.cs | 149 +- .../Shell/NullFeatureProfilesService.cs | 13 +- .../OrchardCore/Shell/RunningShellTable.cs | 237 ++- .../Shell/ServiceCollectionExtensions.cs | 115 +- .../Shell/ShellDescriptorFeaturesManager.cs | 303 ++- .../OrchardCore/Shell/ShellFeaturesManager.cs | 119 +- .../OrchardCore/Shell/ShellHost.cs | 841 ++++---- .../OrchardCore/Shell/ShellOptionsSetup.cs | 43 +- .../OrchardCore/Shell/ShellSettingsManager.cs | 459 +++-- .../Shell/SingleShellSettingsManager.cs | 23 +- .../FeatureAttributeTests.Attribute.cs | 629 +++--- .../Modules/Manifest/FeatureAttributeTests.cs | 803 ++++---- .../ModuleAttributeTests.Attribute.cs | 345 ++-- .../Modules/Manifest/ModuleAttributeTests.cs | 1315 +++++++------ .../Modules/Manifest/ThemeAttributeTests.cs | 1121 ++++++----- .../Modules/StringExtensionsTests.cs | 71 +- .../FluidShapeRenderBenchmark.cs | 189 +- .../MediaTokenServiceBenchmark.cs | 155 +- test/OrchardCore.Benchmarks/Program.cs | 11 +- .../ResourceManagerBenchmark.cs | 75 +- test/OrchardCore.Benchmarks/RuleBenchmark.cs | 83 +- .../ShapeFactoryBenchmark.cs | 87 +- .../ShapeProxyBenchmark.cs | 99 +- test/OrchardCore.Benchmarks/SlugBenchmark.cs | 39 +- .../Support/FakeFileProvider.cs | 27 +- .../Support/FakeWebHostEnvironment.cs | 19 +- .../Examples.Features.AssyAttrib/Root.cs | 13 +- .../BaseThemeSample/DependentTypes.cs | 6 +- .../Examples.Modules.AssyAttrib.Alpha/Root.cs | 13 +- .../Examples.Modules.AssyAttrib.Bravo/Root.cs | 13 +- .../Root.cs | 13 +- .../Examples.OrchardCoreModules.Alpha/Root.cs | 13 +- .../ModuleSample/DependentTypes.cs | 4 +- .../OrchardCore.Application.Pages/Program.cs | 25 +- .../OrchardCore.Application.Pages/Startup.cs | 27 +- .../Module.Pages/Startup.cs | 25 +- .../Theme.Pages/PlaceHolder.cs | 9 +- .../Examples.OrchardCoreThemes.Alpha/Root.cs | 13 +- .../Examples.Themes.AssyAttrib.Alpha/Root.cs | 13 +- .../Examples.Themes.AssyAttrib.Bravo/Root.cs | 13 +- .../Root.cs | 13 +- .../BlogPostApiControllerTests.cs | 523 +++-- .../BlogPostContentStepIdempotentTests.cs | 121 +- .../BlogPostCreateDeploymentPlanTests.cs | 319 ++- .../BlogPostUpdateDeploymentPlanTests.cs | 193 +- .../ContentStepLuceneQueryTests.cs | 79 +- .../Apis/Context/AgencyContext.cs | 17 +- .../Apis/Context/AuthenticationContext.cs | 141 +- .../Apis/Context/BlogContext.cs | 47 +- .../Context/BlogPostApiControllerContext.cs | 51 +- .../Apis/Context/BlogPostDeploymentContext.cs | 133 +- .../Extensions/HttpContentExtensions.cs | 19 +- .../Extensions/HttpRequestExtensions.cs | 367 ++-- .../Apis/Context/MvcTestFixture.cs | 41 +- .../Apis/Context/SiteContext.cs | 315 ++- .../Apis/Context/SiteStartup.cs | 93 +- .../Apis/Context/TablePrefixGenerator.cs | 45 +- .../Apis/Context/TablePrefixGeneratorTests.cs | 25 +- .../Apis/Context/TestRecipeHarvester.cs | 69 +- .../Apis/GraphQL/Blog/BlogPostTests.cs | 533 +++-- .../ContentTypeQueryResourceBuilderTests.cs | 227 ++- .../GraphQL/ContentItemsFieldTypeTests.cs | 909 +++++---- .../GraphQL/Queries/PredicateQueryTests.cs | 2 +- .../Queries/RecentBlogPostsQueryTests.cs | 89 +- .../RequiresPermissionValidationRuleTests.cs | 257 ++- .../Apis/Lucene/LuceneContext.cs | 17 +- .../Apis/Lucene/LuceneQueryTests.cs | 231 ++- .../CommandHandlerDescriptorBuilderTests.cs | 139 +- .../Commands/CommandHandlerTests.cs | 439 +++-- .../Commands/CommandManagerTests.cs | 77 +- .../Data/ContentItemExtensions.cs | 229 ++- .../Data/ContentItemTests.cs | 589 +++--- .../Data/JsonDynamicTests.cs | 4 +- .../DisplayManagement/ArgsUtility.cs | 51 +- .../DisplayManagement/CompositeTests.cs | 105 +- .../BasicShapeTemplateHarvesterTests.cs | 263 ++- .../DefaultShapeTableManagerTests.cs | 793 ++++---- .../Decriptors/LocationParserTests.cs | 549 +++--- .../DefaultDisplayManagerTests.cs | 679 ++++--- .../DisplayManagement/NilTests.cs | 87 +- .../DisplayManagement/ShapeFactoryTests.cs | 197 +- .../DisplayManagement/ShapeHelperTests.cs | 91 +- .../DisplayManagement/ShapeSerializerTests.cs | 83 +- .../Title/PageTitleBuilderTests.cs | 241 ++- .../ViewBufferTextWriterContentTests.cs | 177 +- .../DisplayManagement/ZoneonDemandTests.cs | 369 ++-- .../Email/EmailAddressValidatorTests.cs | 59 +- test/OrchardCore.Tests/Email/EmailTests.cs | 445 +++-- .../Extensions/ExtensionManagerTests.cs | 463 +++-- .../Hosting/Console/CommandParserTests.cs | 359 ++-- .../Html/HtmlSanitizerTests.cs | 101 +- .../Localization/CultureDictionaryTests.cs | 117 +- .../Localization/CultureScopeTests.cs | 105 +- .../DefaultPluralRuleProviderTests.cs | 41 +- .../Localization/LocalizationManagerTests.cs | 109 +- .../Localization/NullHtmlLocalizerTests.cs | 39 +- .../Localization/NullStringLocalizerTests.cs | 73 +- .../Localization/PoParserTests.cs | 413 ++-- ...rtableObjectStringLocalizerFactoryTests.cs | 69 +- .../PortableObjectStringLocalizerTests.cs | 587 +++--- .../DateTimeParserTests.cs | 37 +- .../Feeds/RssFeedTests.cs | 155 +- .../Workflows/EmailTaskTests.cs | 133 +- .../OrchardCore.Markdown/MarkdownTests.cs | 75 +- .../AssetUrlShortcodeTests.cs | 91 +- .../OrchardCore.Media/ImageShortcodeTests.cs | 99 +- .../OrchardCore.Media/MediaEventTests.cs | 111 +- .../OrchardCore.Media/MediaTokenTests.cs | 259 ++- .../ApplicationControllerTests.cs | 305 ++- .../OpenIdApplicationStepTests.cs | 309 ++- .../OpenIdApplicationStepTestsData.cs | 127 +- .../OpenIdScopeStepTests.cs | 295 ++- .../OpenIdServerDeploymentSourceTests.cs | 213 +- .../OrchardCore.Roles/RolesMockHelper.cs | 23 +- .../RolesPermissionsHandlerTests.cs | 231 ++- .../Modules/OrchardCore.Rules/RuleTests.cs | 347 ++-- ...eadersApplicationBuilderExtensionsTests.cs | 173 +- .../SecurityHeadersMiddlewareTests.cs | 235 ++- .../LocaleShortcodeTests.cs | 35 +- .../Services/TenantValidatorTests.cs | 237 ++- .../OrchardCore.Twitter/TwitterClientTests.cs | 185 +- .../Modules/PoweredByMiddlewareTests.cs | 97 +- .../Orchard.Queries/SqlParserTests.cs | 483 +++-- .../CacheScopeManagerTests.cs | 561 +++--- .../AccountControllerTests.cs | 2 +- ...UserClaimsPrincipalProviderFactoryTests.cs | 80 +- .../OrchardCore.Users/UserValidatorTests.cs | 265 ++- .../OrchardCore.Users/UsersMockHelper.cs | 43 +- .../Recipes/RecipeExecutorTests.cs | 165 +- .../ResourceDefinitionTests.cs | 491 +++-- .../ResourceManagerTests.cs | 1549 ++++++++------- .../Routing/AutorouteEntriesTests.cs | 359 ++-- .../Security/PermissionHandlerHelper.cs | 69 +- .../Security/PermissionHandlerTests.cs | 173 +- .../Shell/RunningShellTableTests.cs | 673 ++++--- .../Shell/ShellContainerFactoryTests.cs | 407 ++-- .../Stubs/MemoryFileBuilder.cs | 47 +- .../Stubs/StubExtensionManager.cs | 93 +- .../Stubs/StubHostingEnvironment.cs | 49 +- .../Stubs/StubPoFileLocationProvider.cs | 29 +- .../Stubs/TestShapeBindingResolver.cs | 37 +- .../Tokens.Content/SlugServiceTests.cs | 133 +- .../Utilities/StartupRunner.cs | 27 +- .../Workflows/Activities/AddTask.cs | 81 +- .../Workflows/Activities/WriteLineTask.cs | 67 +- .../Workflows/WorkflowManagerTests.cs | 227 ++- 3583 files changed, 144645 insertions(+), 148199 deletions(-) diff --git a/.editorconfig b/.editorconfig index e591a9cd54c..2cf117a4d88 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,6 @@ root = true [*] -end_of_line = crlf charset = utf-8 indent_style = space indent_size = 4 @@ -32,7 +31,10 @@ dotnet_sort_system_directives_first = true # Code-block preferences csharp_prefer_braces = true csharp_prefer_simple_using_statement = true -csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_namespace_declarations = file_scoped:warning +# Note that currently both IDE* rules and csharp_style_* rules are necessary, because only IDE rules will be enforced +# during build, see: https://github.com/dotnet/roslyn/issues/44201. +dotnet_diagnostic.IDE0161.severity = warning # Range operator csharp_style_prefer_range_operator = false:warning diff --git a/.gitattributes b/.gitattributes index b175f24ba07..afbe3b25f10 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,3 @@ * text=auto **/wwwroot/Scripts/* linguist-vendored - -# Keep LF line endings in webroot assets files. Otherwise, building them under Windows would change the line endings to CLRF and cause changes without actually editing the source files. -**/wwwroot/**/*.js text eol=lf -**/wwwroot/**/*.css text eol=lf diff --git a/Directory.Packages.props b/Directory.Packages.props index a3effff50d5..674edaf3f9c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -56,6 +56,7 @@ + diff --git a/src/OrchardCore.Build/OrchardCore.Commons.props b/src/OrchardCore.Build/OrchardCore.Commons.props index 1b3d5fcbd2c..8679acba8de 100644 --- a/src/OrchardCore.Build/OrchardCore.Commons.props +++ b/src/OrchardCore.Build/OrchardCore.Commons.props @@ -13,7 +13,6 @@ false false false - Orchard Core Framework is an application framework for building modular, multi-tenant applications on ASP.NET Core. Orchard Core CMS is a Web Content Management System (CMS) built on top of the Orchard Core Framework. @@ -48,6 +47,10 @@ + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminAreaControllerRouteMapper.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminAreaControllerRouteMapper.cs index fb6677e135d..5b58e37e7ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminAreaControllerRouteMapper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminAreaControllerRouteMapper.cs @@ -5,53 +5,52 @@ using Microsoft.Extensions.Options; using OrchardCore.Mvc.Routing; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +public class AdminAreaControllerRouteMapper : IAreaControllerRouteMapper { - public class AdminAreaControllerRouteMapper : IAreaControllerRouteMapper + private const string _defaultAreaPattern = "{area}/{controller}/{action}/{id?}"; + private readonly string _adminUrlPrefix; + + public int Order => -1000; + + public AdminAreaControllerRouteMapper(IOptions adminOptions) { - private const string _defaultAreaPattern = "{area}/{controller}/{action}/{id?}"; - private readonly string _adminUrlPrefix; + _adminUrlPrefix = adminOptions.Value.AdminUrlPrefix; + } - public int Order => -1000; + public bool TryMapAreaControllerRoute(IEndpointRouteBuilder routes, ControllerActionDescriptor descriptor) + { + var controllerAttribute = descriptor.ControllerTypeInfo.GetCustomAttribute(); + var actionAttribute = descriptor.MethodInfo.GetCustomAttribute(); - public AdminAreaControllerRouteMapper(IOptions adminOptions) + if (descriptor.ControllerName != "Admin" && controllerAttribute == null && actionAttribute == null) { - _adminUrlPrefix = adminOptions.Value.AdminUrlPrefix; + return false; } - public bool TryMapAreaControllerRoute(IEndpointRouteBuilder routes, ControllerActionDescriptor descriptor) + string name = null; + var pattern = _defaultAreaPattern; + + if (!string.IsNullOrWhiteSpace(actionAttribute?.Template)) { - var controllerAttribute = descriptor.ControllerTypeInfo.GetCustomAttribute(); - var actionAttribute = descriptor.MethodInfo.GetCustomAttribute(); - - if (descriptor.ControllerName != "Admin" && controllerAttribute == null && actionAttribute == null) - { - return false; - } - - string name = null; - var pattern = _defaultAreaPattern; - - if (!string.IsNullOrWhiteSpace(actionAttribute?.Template)) - { - name = actionAttribute.RouteName; - pattern = actionAttribute.Template; - } - else if (!string.IsNullOrWhiteSpace(controllerAttribute?.Template)) - { - name = controllerAttribute.RouteName; - pattern = controllerAttribute.Template; - } - - var (area, controller, action) = RoutingHelper.GetMvcRouteValues(descriptor); - - routes.MapControllerRoute( - name: RoutingHelper.ReplaceMvcPlaceholders(name, area, controller, action) ?? descriptor.DisplayName, - pattern: $"{_adminUrlPrefix}/{RoutingHelper.ReplaceMvcPlaceholders(pattern.TrimStart('/'), area, controller, action)}", - defaults: new { area, controller, action } - ); - - return true; + name = actionAttribute.RouteName; + pattern = actionAttribute.Template; } + else if (!string.IsNullOrWhiteSpace(controllerAttribute?.Template)) + { + name = controllerAttribute.RouteName; + pattern = controllerAttribute.Template; + } + + var (area, controller, action) = RoutingHelper.GetMvcRouteValues(descriptor); + + routes.MapControllerRoute( + name: RoutingHelper.ReplaceMvcPlaceholders(name, area, controller, action) ?? descriptor.DisplayName, + pattern: $"{_adminUrlPrefix}/{RoutingHelper.ReplaceMvcPlaceholders(pattern.TrimStart('/'), area, controller, action)}", + defaults: new { area, controller, action } + ); + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminFilter.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminFilter.cs index 9c9197e0822..b48f79ddbcf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminFilter.cs @@ -4,63 +4,62 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +/// +/// Intercepts any request to check whether it applies to the admin site. +/// If so it marks the request as such and ensures the user as the right to access it. +/// +public sealed class AdminFilter : ActionFilterAttribute, IAsyncPageFilter { - /// - /// Intercepts any request to check whether it applies to the admin site. - /// If so it marks the request as such and ensures the user as the right to access it. - /// - public sealed class AdminFilter : ActionFilterAttribute, IAsyncPageFilter + private readonly IAuthorizationService _authorizationService; + + public AdminFilter(IAuthorizationService authorizationService) { - private readonly IAuthorizationService _authorizationService; + _authorizationService = authorizationService; + } - public AdminFilter(IAuthorizationService authorizationService) - { - _authorizationService = authorizationService; - } + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + ArgumentNullException.ThrowIfNull(context); - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + if (!await AuthorizeAsync(context.HttpContext)) { - ArgumentNullException.ThrowIfNull(context); + context.Result = context.HttpContext.User?.Identity?.IsAuthenticated ?? false + ? new ForbidResult() + : new ChallengeResult(); + return; + } - if (!await AuthorizeAsync(context.HttpContext)) - { - context.Result = context.HttpContext.User?.Identity?.IsAuthenticated ?? false - ? new ForbidResult() - : new ChallengeResult(); - return; - } + await base.OnActionExecutionAsync(context, next); + } - await base.OnActionExecutionAsync(context, next); - } + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + ArgumentNullException.ThrowIfNull(context); - public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + if (!await AuthorizeAsync(context.HttpContext)) { - ArgumentNullException.ThrowIfNull(context); + context.Result = new ChallengeResult(); + return; + } - if (!await AuthorizeAsync(context.HttpContext)) - { - context.Result = new ChallengeResult(); - return; - } + // Do post work. + await next.Invoke(); + } - // Do post work. - await next.Invoke(); - } + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } - public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + private Task AuthorizeAsync(Microsoft.AspNetCore.Http.HttpContext context) + { + if (AdminAttribute.IsApplied(context)) { - return Task.CompletedTask; + return _authorizationService.AuthorizeAsync(context.User, AdminPermissions.AccessAdminPanel); } - private Task AuthorizeAsync(Microsoft.AspNetCore.Http.HttpContext context) - { - if (AdminAttribute.IsApplied(context)) - { - return _authorizationService.AuthorizeAsync(context.User, AdminPermissions.AccessAdminPanel); - } - - return Task.FromResult(true); - } + return Task.FromResult(true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenu.cs index 5a38b1e0527..ebd71358a49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenu.cs @@ -4,43 +4,42 @@ using OrchardCore.Admin.Drivers; using OrchardCore.Navigation; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", AdminSiteSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", AdminSiteSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Admin"], S["Admin"].PrefixPosition(), admin => admin - .AddClass("admin").Id("admin") - .Action("Index", "Admin", _routeValues) - .Permission(PermissionsAdminSettings.ManageAdminSettings) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Admin"], S["Admin"].PrefixPosition(), admin => admin + .AddClass("admin").Id("admin") + .Action("Index", "Admin", _routeValues) + .Permission(PermissionsAdminSettings.ManageAdminSettings) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenuFilter.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenuFilter.cs index 00d313ea21f..6f8d1bd28a0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenuFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminMenuFilter.cs @@ -5,73 +5,72 @@ using OrchardCore.DisplayManagement.Shapes; using OrchardCore.Navigation; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +/// +/// This filter inject a Navigation shape in the Navigation zone of the Layout +/// for any ViewResult returned from an Admin controller. +/// +public sealed class AdminMenuFilter : IAsyncResultFilter { - /// - /// This filter inject a Navigation shape in the Navigation zone of the Layout - /// for any ViewResult returned from an Admin controller. - /// - public sealed class AdminMenuFilter : IAsyncResultFilter + private readonly ILayoutAccessor _layoutAccessor; + private readonly IShapeFactory _shapeFactory; + + public AdminMenuFilter(ILayoutAccessor layoutAccessor, + IShapeFactory shapeFactory) { - private readonly ILayoutAccessor _layoutAccessor; - private readonly IShapeFactory _shapeFactory; + _layoutAccessor = layoutAccessor; + _shapeFactory = shapeFactory; + } - public AdminMenuFilter(ILayoutAccessor layoutAccessor, - IShapeFactory shapeFactory) + public async Task OnResultExecutionAsync(ResultExecutingContext filterContext, ResultExecutionDelegate next) + { + // Should only run on a full view rendering result + if (!filterContext.IsViewOrPageResult()) { - _layoutAccessor = layoutAccessor; - _shapeFactory = shapeFactory; + await next(); + return; } - public async Task OnResultExecutionAsync(ResultExecutingContext filterContext, ResultExecutionDelegate next) + // Should only run on the Admin + if (!AdminAttribute.IsApplied(filterContext.HttpContext)) { - // Should only run on a full view rendering result - if (!filterContext.IsViewOrPageResult()) - { - await next(); - return; - } + await next(); + return; + } - // Should only run on the Admin - if (!AdminAttribute.IsApplied(filterContext.HttpContext)) - { - await next(); - return; - } + // Should only run for authenticated users + if (!(filterContext.HttpContext.User?.Identity?.IsAuthenticated ?? false)) + { + await next(); + return; + } - // Should only run for authenticated users - if (!(filterContext.HttpContext.User?.Identity?.IsAuthenticated ?? false)) - { - await next(); - return; - } + // Don't create the menu if the status code is 3xx + var statusCode = filterContext.HttpContext.Response.StatusCode; + if (statusCode >= 300 && statusCode < 400) + { + await next(); + return; + } - // Don't create the menu if the status code is 3xx - var statusCode = filterContext.HttpContext.Response.StatusCode; - if (statusCode >= 300 && statusCode < 400) + // Populate main nav + var menuShape = await _shapeFactory.CreateAsync("Navigation", + Arguments.From(new { - await next(); - return; - } - - // Populate main nav - var menuShape = await _shapeFactory.CreateAsync("Navigation", - Arguments.From(new - { - MenuName = NavigationConstants.AdminId, - filterContext.RouteData, - })); + MenuName = NavigationConstants.AdminId, + filterContext.RouteData, + })); - var layout = await _layoutAccessor.GetLayoutAsync(); + var layout = await _layoutAccessor.GetLayoutAsync(); - var navigation = layout.Zones["Navigation"]; + var navigation = layout.Zones["Navigation"]; - if (navigation is Shape shape) - { - await shape.AddAsync(menuShape); - } - - await next(); + if (navigation is Shape shape) + { + await shape.AddAsync(menuShape); } + + await next(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelConvention.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelConvention.cs index 3ff8e6a0b50..852397c5cc1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelConvention.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelConvention.cs @@ -1,35 +1,34 @@ using System.Linq; using Microsoft.AspNetCore.Mvc.ApplicationModels; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +public class AdminPageRouteModelConvention : IPageRouteModelConvention { - public class AdminPageRouteModelConvention : IPageRouteModelConvention + private readonly string _adminUrlPrefixTemplate; + + public AdminPageRouteModelConvention(string adminUrlPrefix) + { + _adminUrlPrefixTemplate = adminUrlPrefix; + } + + public void Apply(PageRouteModel model) { - private readonly string _adminUrlPrefixTemplate; + var route = model.Selectors.FirstOrDefault()?.AttributeRouteModel; - public AdminPageRouteModelConvention(string adminUrlPrefix) + if (route == null) { - _adminUrlPrefixTemplate = adminUrlPrefix; + return; } - public void Apply(PageRouteModel model) + if (!route.Template.StartsWith(model.AreaName)) { - var route = model.Selectors.FirstOrDefault()?.AttributeRouteModel; - - if (route == null) - { - return; - } - - if (!route.Template.StartsWith(model.AreaName)) - { - return; - } + return; + } - if (route.Template.Contains("/Admin/") || model.Properties.ContainsKey("Admin")) - { - route.Template = AttributeRouteModel.CombineTemplates(_adminUrlPrefixTemplate, route.Template); - } + if (route.Template.Contains("/Admin/") || model.Properties.ContainsKey("Admin")) + { + route.Template = AttributeRouteModel.CombineTemplates(_adminUrlPrefixTemplate, route.Template); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelProvider.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelProvider.cs index 4ac4f126eb7..f76a60dfd63 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminPageRouteModelProvider.cs @@ -10,98 +10,97 @@ using Microsoft.Extensions.Hosting; using OrchardCore.Mvc; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +internal sealed class AdminPageRouteModelProvider : IPageRouteModelProvider { - internal sealed class AdminPageRouteModelProvider : IPageRouteModelProvider - { - private readonly IHostEnvironment _hostingEnvironment; - private readonly ApplicationPartManager _applicationManager; + private readonly IHostEnvironment _hostingEnvironment; + private readonly ApplicationPartManager _applicationManager; - public AdminPageRouteModelProvider(IHostEnvironment hostingEnvironment, ApplicationPartManager applicationManager) - { - _hostingEnvironment = hostingEnvironment; - _applicationManager = applicationManager; - } + public AdminPageRouteModelProvider(IHostEnvironment hostingEnvironment, ApplicationPartManager applicationManager) + { + _hostingEnvironment = hostingEnvironment; + _applicationManager = applicationManager; + } - public int Order => 1000; + public int Order => 1000; - public void OnProvidersExecuting(PageRouteModelProviderContext context) - { - ArgumentNullException.ThrowIfNull(context); + public void OnProvidersExecuting(PageRouteModelProviderContext context) + { + ArgumentNullException.ThrowIfNull(context); - IEnumerable descriptors; + IEnumerable descriptors; - var refsFolderExists = Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "refs")); + var refsFolderExists = Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "refs")); - if (_hostingEnvironment.IsDevelopment() && refsFolderExists) - { - descriptors = GetPageDescriptors(_applicationManager); - } - else - { - descriptors = GetPageDescriptors(_applicationManager); - } + if (_hostingEnvironment.IsDevelopment() && refsFolderExists) + { + descriptors = GetPageDescriptors(_applicationManager); + } + else + { + descriptors = GetPageDescriptors(_applicationManager); + } - var adminPaths = new HashSet(StringComparer.OrdinalIgnoreCase); + var adminPaths = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var descriptor in descriptors) + foreach (var descriptor in descriptors) + { + foreach (var type in descriptor.Type.GetNestedTypes()) { - foreach (var type in descriptor.Type.GetNestedTypes()) - { - var attribute = type.GetCustomAttribute(); - - if (attribute != null) - { - adminPaths.Add(descriptor.RelativePath); - break; - } - } - } + var attribute = type.GetCustomAttribute(); - foreach (var model in context.RouteModels.ToArray()) - { - if (adminPaths.Contains(model.RelativePath)) + if (attribute != null) { - model.Properties["Admin"] = null; + adminPaths.Add(descriptor.RelativePath); + break; } } } - public void OnProvidersExecuted(PageRouteModelProviderContext context) + foreach (var model in context.RouteModels.ToArray()) { + if (adminPaths.Contains(model.RelativePath)) + { + model.Properties["Admin"] = null; + } } + } - private static IEnumerable GetPageDescriptors(ApplicationPartManager applicationManager) - where T : ViewsFeature, new() - { - ArgumentNullException.ThrowIfNull(applicationManager); + public void OnProvidersExecuted(PageRouteModelProviderContext context) + { + } - var viewsFeature = GetViewFeature(applicationManager); + private static IEnumerable GetPageDescriptors(ApplicationPartManager applicationManager) + where T : ViewsFeature, new() + { + ArgumentNullException.ThrowIfNull(applicationManager); - var visited = new HashSet(StringComparer.OrdinalIgnoreCase); + var viewsFeature = GetViewFeature(applicationManager); - foreach (var viewDescriptor in viewsFeature.ViewDescriptors) + var visited = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var viewDescriptor in viewsFeature.ViewDescriptors) + { + if (!visited.Add(viewDescriptor.RelativePath)) { - if (!visited.Add(viewDescriptor.RelativePath)) - { - continue; - } + continue; + } - if (IsRazorPage(viewDescriptor)) - { - yield return viewDescriptor; - } + if (IsRazorPage(viewDescriptor)) + { + yield return viewDescriptor; } } + } - private static bool IsRazorPage(CompiledViewDescriptor viewDescriptor) => - viewDescriptor.Item?.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind; + private static bool IsRazorPage(CompiledViewDescriptor viewDescriptor) => + viewDescriptor.Item?.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind; - private static T GetViewFeature(ApplicationPartManager applicationManager) where T : ViewsFeature, new() - { - var viewsFeature = new T(); - applicationManager.PopulateFeature(viewsFeature); - return viewsFeature; - } + private static T GetViewFeature(ApplicationPartManager applicationManager) where T : ViewsFeature, new() + { + var viewsFeature = new T(); + applicationManager.PopulateFeature(viewsFeature); + return viewsFeature; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeSelector.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeSelector.cs index a5c3659139b..ade57f59e8b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeSelector.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeSelector.cs @@ -2,45 +2,44 @@ using Microsoft.AspNetCore.Http; using OrchardCore.DisplayManagement.Theming; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +/// +/// Provides the theme defined in the site configuration for the current scope (request). +/// The same is returned if called multiple times +/// during the same scope. +/// +public class AdminThemeSelector : IThemeSelector { - /// - /// Provides the theme defined in the site configuration for the current scope (request). - /// The same is returned if called multiple times - /// during the same scope. - /// - public class AdminThemeSelector : IThemeSelector - { - private readonly IAdminThemeService _adminThemeService; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAdminThemeService _adminThemeService; + private readonly IHttpContextAccessor _httpContextAccessor; - public AdminThemeSelector( - IAdminThemeService adminThemeService, - IHttpContextAccessor httpContextAccessor - ) - { - _adminThemeService = adminThemeService; - _httpContextAccessor = httpContextAccessor; - } + public AdminThemeSelector( + IAdminThemeService adminThemeService, + IHttpContextAccessor httpContextAccessor + ) + { + _adminThemeService = adminThemeService; + _httpContextAccessor = httpContextAccessor; + } - public async Task GetThemeAsync() + public async Task GetThemeAsync() + { + if (AdminAttribute.IsApplied(_httpContextAccessor.HttpContext)) { - if (AdminAttribute.IsApplied(_httpContextAccessor.HttpContext)) + var adminThemeName = await _adminThemeService.GetAdminThemeNameAsync(); + if (string.IsNullOrEmpty(adminThemeName)) { - var adminThemeName = await _adminThemeService.GetAdminThemeNameAsync(); - if (string.IsNullOrEmpty(adminThemeName)) - { - return null; - } - - return new ThemeSelectorResult - { - Priority = 100, - ThemeName = adminThemeName - }; + return null; } - return null; + return new ThemeSelectorResult + { + Priority = 100, + ThemeName = adminThemeName + }; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeService.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeService.cs index 08a0f538e8e..8d3e75d1fd8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminThemeService.cs @@ -2,43 +2,42 @@ using OrchardCore.Environment.Extensions; using OrchardCore.Settings; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +public class AdminThemeService : IAdminThemeService { - public class AdminThemeService : IAdminThemeService + private readonly ISiteService _siteService; + private readonly IExtensionManager _extensionManager; + + public AdminThemeService( + ISiteService siteService, + IExtensionManager extensionManager) { - private readonly ISiteService _siteService; - private readonly IExtensionManager _extensionManager; + _siteService = siteService; + _extensionManager = extensionManager; + } - public AdminThemeService( - ISiteService siteService, - IExtensionManager extensionManager) + public async Task GetAdminThemeAsync() + { + var currentThemeName = await GetAdminThemeNameAsync(); + if (string.IsNullOrEmpty(currentThemeName)) { - _siteService = siteService; - _extensionManager = extensionManager; + return null; } - public async Task GetAdminThemeAsync() - { - var currentThemeName = await GetAdminThemeNameAsync(); - if (string.IsNullOrEmpty(currentThemeName)) - { - return null; - } - - return _extensionManager.GetExtension(currentThemeName); - } + return _extensionManager.GetExtension(currentThemeName); + } - public async Task SetAdminThemeAsync(string themeName) - { - var site = await _siteService.LoadSiteSettingsAsync(); - site.Properties["CurrentAdminThemeName"] = themeName; - await _siteService.UpdateSiteSettingsAsync(site); - } + public async Task SetAdminThemeAsync(string themeName) + { + var site = await _siteService.LoadSiteSettingsAsync(); + site.Properties["CurrentAdminThemeName"] = themeName; + await _siteService.UpdateSiteSettingsAsync(site); + } - public async Task GetAdminThemeNameAsync() - { - var site = await _siteService.GetSiteSettingsAsync(); - return (string)site.Properties["CurrentAdminThemeName"]; - } + public async Task GetAdminThemeNameAsync() + { + var site = await _siteService.GetSiteSettingsAsync(); + return (string)site.Properties["CurrentAdminThemeName"]; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/AdminZoneFilter.cs b/src/OrchardCore.Modules/OrchardCore.Admin/AdminZoneFilter.cs index 63abff0170b..d40a4575467 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/AdminZoneFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/AdminZoneFilter.cs @@ -3,32 +3,31 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +/// +/// This filter makes an controller that starts with Admin and Razor Pages in /Pages/Admin folder behave as +/// it had the . +/// +public sealed class AdminZoneFilter : IAsyncResourceFilter { - /// - /// This filter makes an controller that starts with Admin and Razor Pages in /Pages/Admin folder behave as - /// it had the . - /// - public sealed class AdminZoneFilter : IAsyncResourceFilter + public Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { - public Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) + if (context.ActionDescriptor is ControllerActionDescriptor action) { - if (context.ActionDescriptor is ControllerActionDescriptor action) + if (action.ControllerName == "Admin") { - if (action.ControllerName == "Admin") - { - AdminAttribute.Apply(context.HttpContext); - } + AdminAttribute.Apply(context.HttpContext); } - else if (context.ActionDescriptor is PageActionDescriptor page) + } + else if (context.ActionDescriptor is PageActionDescriptor page) + { + if (page.ViewEnginePath.Contains("/Admin/")) { - if (page.ViewEnginePath.Contains("/Admin/")) - { - AdminAttribute.Apply(context.HttpContext); - } + AdminAttribute.Apply(context.HttpContext); } - - return next(); } + + return next(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Admin/Controllers/AdminController.cs index 0d020a8dc5b..95687dd2bdd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/Controllers/AdminController.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.Admin.Controllers +namespace OrchardCore.Admin.Controllers; + +[Authorize] +public class AdminController : Controller { - [Authorize] - public class AdminController : Controller + public IActionResult Index() { - public IActionResult Index() - { - return View(); - } + return View(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/Drivers/AdminSiteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Admin/Drivers/AdminSiteSettingsDisplayDriver.cs index 73b9ff6e8e1..ef8fb39f354 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/Drivers/AdminSiteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/Drivers/AdminSiteSettingsDisplayDriver.cs @@ -9,64 +9,63 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.Admin.Drivers +namespace OrchardCore.Admin.Drivers; + +public sealed class AdminSiteSettingsDisplayDriver : SiteDisplayDriver { - public sealed class AdminSiteSettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "admin"; + + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public AdminSiteSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - public const string GroupId = "admin"; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + protected override string SettingsGroupId + => GroupId; - public AdminSiteSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + public override async Task EditAsync(ISite site, AdminSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, PermissionsAdminSettings.ManageAdminSettings)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => GroupId; - - public override async Task EditAsync(ISite site, AdminSettings settings, BuildEditorContext context) + return Initialize("AdminSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, PermissionsAdminSettings.ManageAdminSettings)) - { - return null; - } + model.DisplayThemeToggler = settings.DisplayThemeToggler; + model.DisplayMenuFilter = settings.DisplayMenuFilter; + model.DisplayNewMenu = settings.DisplayNewMenu; + model.DisplayTitlesInTopbar = settings.DisplayTitlesInTopbar; + }).Location("Content:3") + .OnGroup(SettingsGroupId); + } - return Initialize("AdminSettings_Edit", model => - { - model.DisplayThemeToggler = settings.DisplayThemeToggler; - model.DisplayMenuFilter = settings.DisplayMenuFilter; - model.DisplayNewMenu = settings.DisplayNewMenu; - model.DisplayTitlesInTopbar = settings.DisplayTitlesInTopbar; - }).Location("Content:3") - .OnGroup(SettingsGroupId); - } + public override async Task UpdateAsync(ISite site, AdminSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task UpdateAsync(ISite site, AdminSettings settings, UpdateEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, PermissionsAdminSettings.ManageAdminSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, PermissionsAdminSettings.ManageAdminSettings)) - { - return null; - } + return null; + } - var model = new AdminSettingsViewModel(); + var model = new AdminSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.DisplayThemeToggler = model.DisplayThemeToggler; - settings.DisplayMenuFilter = model.DisplayMenuFilter; - settings.DisplayNewMenu = model.DisplayNewMenu; - settings.DisplayTitlesInTopbar = model.DisplayTitlesInTopbar; + settings.DisplayThemeToggler = model.DisplayThemeToggler; + settings.DisplayMenuFilter = model.DisplayMenuFilter; + settings.DisplayNewMenu = model.DisplayNewMenu; + settings.DisplayTitlesInTopbar = model.DisplayTitlesInTopbar; - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs index d52f4a06e78..821441a2107 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs @@ -26,102 +26,101 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.Admin +namespace OrchardCore.Admin; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly AdminOptions _adminOptions; + private readonly IShellConfiguration _configuration; + + public Startup(IOptions adminOptions, IShellConfiguration configuration) { - private readonly AdminOptions _adminOptions; - private readonly IShellConfiguration _configuration; + _adminOptions = adminOptions.Value; + _configuration = configuration; + } - public Startup(IOptions adminOptions, IShellConfiguration configuration) - { - _adminOptions = adminOptions.Value; - _configuration = configuration; - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddNavigation(); - public override void ConfigureServices(IServiceCollection services) + services.Configure((options) => { - services.AddNavigation(); + options.Filters.Add(); + options.Filters.Add(); - services.Configure((options) => - { - options.Filters.Add(); - options.Filters.Add(); - - // Ordered to be called before any global filter. - options.Filters.Add(-1000); - }); - - services.AddTransient(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, AdminSiteSettingsDisplayDriver>(); - services.AddScoped(); - services.AddScoped(); - services.AddSingleton(); - services.AddScoped, VisitSiteNavbarDisplayDriver>(); - - services.Configure(_configuration.GetSection("OrchardCore_Admin")); - } - - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "Admin", - areaName: "OrchardCore.Admin", - pattern: _adminOptions.AdminUrlPrefix, - defaults: new { controller = typeof(AdminController).ControllerName(), action = nameof(AdminController.Index) } - ); - } + // Ordered to be called before any global filter. + options.Filters.Add(-1000); + }); + + services.AddTransient(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, AdminSiteSettingsDisplayDriver>(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddScoped, VisitSiteNavbarDisplayDriver>(); + + services.Configure(_configuration.GetSection("OrchardCore_Admin")); } - public sealed class AdminPagesStartup : StartupBase + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - public override int Order - => OrchardCoreConstants.ConfigureOrder.AdminPages; + routes.MapAreaControllerRoute( + name: "Admin", + areaName: "OrchardCore.Admin", + pattern: _adminOptions.AdminUrlPrefix, + defaults: new { controller = typeof(AdminController).ControllerName(), action = nameof(AdminController.Index) } + ); + } +} + +public sealed class AdminPagesStartup : StartupBase +{ + public override int Order + => OrchardCoreConstants.ConfigureOrder.AdminPages; - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.Configure((options) => { - services.Configure((options) => - { - var adminOptions = ShellScope.Services.GetRequiredService>().Value; - options.Conventions.Add(new AdminPageRouteModelConvention(adminOptions.AdminUrlPrefix)); - }); - } + var adminOptions = ShellScope.Services.GetRequiredService>().Value; + options.Conventions.Add(new AdminPageRouteModelConvention(adminOptions.AdminUrlPrefix)); + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Admin settings"], S => S["Exports the admin settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Admin settings"], S => S["Exports the admin settings."]); } +} - [RequireFeatures("OrchardCore.Liquid")] - public sealed class LiquidStartup : StartupBase +[RequireFeatures("OrchardCore.Liquid")] +public sealed class LiquidStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => + o.Scope.SetValue(nameof(Navbar), new FunctionValue(async (args, ctx) => { - o.Scope.SetValue(nameof(Navbar), new FunctionValue(async (args, ctx) => + if (ctx is LiquidTemplateContext context) { - if (ctx is LiquidTemplateContext context) - { - var displayManager = context.Services.GetRequiredService>(); - var updateModelAccessor = context.Services.GetRequiredService(); + var displayManager = context.Services.GetRequiredService>(); + var updateModelAccessor = context.Services.GetRequiredService(); - var shape = await displayManager.BuildDisplayAsync(updateModelAccessor.ModelUpdater); + var shape = await displayManager.BuildDisplayAsync(updateModelAccessor.ModelUpdater); - return FluidValue.Create(shape, ctx.Options); - } + return FluidValue.Create(shape, ctx.Options); + } - return NilValue.Instance; - })); - }); - } + return NilValue.Instance; + })); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Admin/ViewModels/AdminSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Admin/ViewModels/AdminSettingsViewModel.cs index f0149d2b2f5..d94df30eaaf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Admin/ViewModels/AdminSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Admin/ViewModels/AdminSettingsViewModel.cs @@ -1,13 +1,12 @@ -namespace OrchardCore.Admin.ViewModels +namespace OrchardCore.Admin.ViewModels; + +public class AdminSettingsViewModel { - public class AdminSettingsViewModel - { - public bool DisplayThemeToggler { get; set; } + public bool DisplayThemeToggler { get; set; } - public bool DisplayMenuFilter { get; set; } + public bool DisplayMenuFilter { get; set; } - public bool DisplayNewMenu { get; set; } + public bool DisplayNewMenu { get; set; } - public bool DisplayTitlesInTopbar { get; set; } - } + public bool DisplayTitlesInTopbar { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Controllers/DashboardController.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Controllers/DashboardController.cs index 6fc1b3d8ee1..31d0ea22f2f 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Controllers/DashboardController.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Controllers/DashboardController.cs @@ -16,196 +16,195 @@ using OrchardCore.Contents; using OrchardCore.DisplayManagement.ModelBinding; -namespace OrchardCore.AdminDashboard.Controllers +namespace OrchardCore.AdminDashboard.Controllers; + +[Admin] +public class DashboardController : Controller { - [Admin] - public class DashboardController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IAdminDashboardService _adminDashboardService; + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly YesSql.ISession _session; + + public DashboardController( + IAuthorizationService authorizationService, + IAdminDashboardService adminDashboardService, + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + IContentDefinitionManager contentDefinitionManager, + IUpdateModelAccessor updateModelAccessor, + YesSql.ISession session) { - private readonly IAuthorizationService _authorizationService; - private readonly IAdminDashboardService _adminDashboardService; - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly YesSql.ISession _session; - - public DashboardController( - IAuthorizationService authorizationService, - IAdminDashboardService adminDashboardService, - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - IContentDefinitionManager contentDefinitionManager, - IUpdateModelAccessor updateModelAccessor, - YesSql.ISession session) + _authorizationService = authorizationService; + _adminDashboardService = adminDashboardService; + _contentManager = contentManager; + _contentItemDisplayManager = contentItemDisplayManager; + _contentDefinitionManager = contentDefinitionManager; + _updateModelAccessor = updateModelAccessor; + _session = session; + } + + public async Task Index() + { + var model = new AdminDashboardViewModel() { - _authorizationService = authorizationService; - _adminDashboardService = adminDashboardService; - _contentManager = contentManager; - _contentItemDisplayManager = contentItemDisplayManager; - _contentDefinitionManager = contentDefinitionManager; - _updateModelAccessor = updateModelAccessor; - _session = session; - } + CanManageDashboard = await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard), + }; - public async Task Index() + if (model.CanManageDashboard || await _authorizationService.AuthorizeAsync(User, Permissions.AccessAdminDashboard)) { - var model = new AdminDashboardViewModel() - { - CanManageDashboard = await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard), - }; + var wrappers = new List(); + var widgetContentTypes = await GetDashboardWidgetsAsync(); - if (model.CanManageDashboard || await _authorizationService.AuthorizeAsync(User, Permissions.AccessAdminDashboard)) + var widgets = await _adminDashboardService.GetWidgetsAsync(x => x.Published); + foreach (var widget in widgets) { - var wrappers = new List(); - var widgetContentTypes = await GetDashboardWidgetsAsync(); + if (!widgetContentTypes.ContainsKey(widget.ContentType)) + { + continue; + } - var widgets = await _adminDashboardService.GetWidgetsAsync(x => x.Published); - foreach (var widget in widgets) + if (!model.CanManageDashboard && !await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, widget)) { - if (!widgetContentTypes.ContainsKey(widget.ContentType)) - { - continue; - } - - if (!model.CanManageDashboard && !await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, widget)) - { - continue; - } - - wrappers.Add(new DashboardWrapper - { - Dashboard = widget, - Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin") - }); + continue; } - model.Dashboards = wrappers.ToArray(); + wrappers.Add(new DashboardWrapper + { + Dashboard = widget, + Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin") + }); } - return View(model); + model.Dashboards = wrappers.ToArray(); } - [Admin("dashboard/manage", "AdminDashboard")] - public async Task Manage() + return View(model); + } + + [Admin("dashboard/manage", "AdminDashboard")] + public async Task Manage() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard)) - { - return Forbid(); - } + return Forbid(); + } - // Set Manage Dashboard Feature. - Request.HttpContext.Features.Set(new DashboardFeature() - { - IsManageRequest = true - }); + // Set Manage Dashboard Feature. + Request.HttpContext.Features.Set(new DashboardFeature() + { + IsManageRequest = true + }); - var dashboardCreatable = new List(); - var widgetContentTypes = await GetDashboardWidgetsAsync(); + var dashboardCreatable = new List(); + var widgetContentTypes = await GetDashboardWidgetsAsync(); - var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); - foreach (var ctd in widgetContentTypes.Values.OrderBy(x => x.DisplayName)) + foreach (var ctd in widgetContentTypes.Values.OrderBy(x => x.DisplayName)) + { + if (!await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, ctd.Name, userId)) { - if (!await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, ctd.Name, userId)) - { - continue; - } - - dashboardCreatable.Add(new SelectListItem(ctd.DisplayName, ctd.Name)); + continue; } - var widgets = await _adminDashboardService.GetWidgetsAsync(x => x.Latest); - var wrappers = new List(); - foreach (var widget in widgets) - { - if (!widgetContentTypes.ContainsKey(widget.ContentType) - || !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, widget.ContentType, userId)) - { - continue; - } - - var wrapper = new DashboardWrapper - { - Dashboard = widget, - Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin") - }; + dashboardCreatable.Add(new SelectListItem(ctd.DisplayName, ctd.Name)); + } - wrappers.Add(wrapper); + var widgets = await _adminDashboardService.GetWidgetsAsync(x => x.Latest); + var wrappers = new List(); + foreach (var widget in widgets) + { + if (!widgetContentTypes.ContainsKey(widget.ContentType) + || !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, widget.ContentType, userId)) + { + continue; } - var model = new AdminDashboardViewModel + var wrapper = new DashboardWrapper { - Dashboards = wrappers.ToArray(), - Creatable = dashboardCreatable + Dashboard = widget, + Content = await _contentItemDisplayManager.BuildDisplayAsync(widget, _updateModelAccessor.ModelUpdater, "DetailAdmin") }; - return View(model); + wrappers.Add(wrapper); } - [HttpPost] - public async Task Update([FromForm] DashboardPartViewModel[] parts) + var model = new AdminDashboardViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard)) - { - return Unauthorized(); - } + Dashboards = wrappers.ToArray(), + Creatable = dashboardCreatable + }; - var contentItemIds = parts.Select(i => i.ContentItemId).ToArray(); + return View(model); + } - // Load the latest version first if any. - var latestItems = await _contentManager.GetAsync(contentItemIds, VersionOptions.Latest); + [HttpPost] + public async Task Update([FromForm] DashboardPartViewModel[] parts) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminDashboard)) + { + return Unauthorized(); + } - if (latestItems == null) - { - return NotFound(); - } + var contentItemIds = parts.Select(i => i.ContentItemId).ToArray(); + + // Load the latest version first if any. + var latestItems = await _contentManager.GetAsync(contentItemIds, VersionOptions.Latest); + + if (latestItems == null) + { + return NotFound(); + } - var publishedItems = await _contentManager.GetAsync(contentItemIds, VersionOptions.Published); + var publishedItems = await _contentManager.GetAsync(contentItemIds, VersionOptions.Published); - foreach (var contentItem in latestItems) + foreach (var contentItem in latestItems) + { + var dashboardPart = contentItem.As(); + if (dashboardPart == null) { - var dashboardPart = contentItem.As(); - if (dashboardPart == null) - { - return Forbid(); - } + return Forbid(); + } - var partViewModel = parts.FirstOrDefault(m => m.ContentItemId == contentItem.ContentItemId); + var partViewModel = parts.FirstOrDefault(m => m.ContentItemId == contentItem.ContentItemId); - dashboardPart.Position = partViewModel?.Position ?? 0; - dashboardPart.Width = partViewModel?.Width ?? 1; - dashboardPart.Height = partViewModel?.Height ?? 1; + dashboardPart.Position = partViewModel?.Position ?? 0; + dashboardPart.Width = partViewModel?.Width ?? 1; + dashboardPart.Height = partViewModel?.Height ?? 1; - contentItem.Apply(dashboardPart); + contentItem.Apply(dashboardPart); - await _session.SaveAsync(contentItem); + await _session.SaveAsync(contentItem); - if (contentItem.IsPublished() == false) + if (contentItem.IsPublished() == false) + { + var publishedVersion = publishedItems.FirstOrDefault(p => p.ContentItemId == contentItem.ContentItemId); + var publishedMetaData = publishedVersion?.As(); + if (publishedMetaData != null) { - var publishedVersion = publishedItems.FirstOrDefault(p => p.ContentItemId == contentItem.ContentItemId); - var publishedMetaData = publishedVersion?.As(); - if (publishedMetaData != null) - { - publishedMetaData.Position = partViewModel.Position; - publishedMetaData.Width = partViewModel.Width; - publishedMetaData.Height = partViewModel.Height; - publishedVersion.Apply(publishedMetaData); - await _session.SaveAsync(publishedVersion); - } + publishedMetaData.Position = partViewModel.Position; + publishedMetaData.Width = partViewModel.Width; + publishedMetaData.Height = partViewModel.Height; + publishedVersion.Apply(publishedMetaData); + await _session.SaveAsync(publishedVersion); } } + } - if (Request.Headers != null && Request.Headers.XRequestedWith == "XMLHttpRequest") - { - return Ok(); - } - - return RedirectToAction(nameof(Manage)); + if (Request.Headers != null && Request.Headers.XRequestedWith == "XMLHttpRequest") + { + return Ok(); } - private async Task> GetDashboardWidgetsAsync() - => (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(t => t.StereotypeEquals("DashboardWidget")) - .ToDictionary(ctd => ctd.Name, ctd => ctd); + return RedirectToAction(nameof(Manage)); } + + private async Task> GetDashboardWidgetsAsync() + => (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(t => t.StereotypeEquals("DashboardWidget")) + .ToDictionary(ctd => ctd.Name, ctd => ctd); } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/DashboardFeature.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/DashboardFeature.cs index 7a2c03bec42..02dc15fc564 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/DashboardFeature.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/DashboardFeature.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.AdminDashboard +namespace OrchardCore.AdminDashboard; + +public class DashboardFeature { - public class DashboardFeature - { - public bool IsManageRequest { get; set; } - } + public bool IsManageRequest { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardContentDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardContentDisplayDriver.cs index 214069b6a7e..fc4b36422d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardContentDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardContentDisplayDriver.cs @@ -10,99 +10,98 @@ using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AdminDashboard.Drivers +namespace OrchardCore.AdminDashboard.Drivers; + +public sealed class DashboardContentDisplayDriver : ContentDisplayDriver { - public sealed class DashboardContentDisplayDriver : ContentDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IContentManager _contentManager; + + public DashboardContentDisplayDriver(IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IContentManager contentManager) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IContentManager _contentManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _contentManager = contentManager; + } - public DashboardContentDisplayDriver(IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IContentManager contentManager) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _contentManager = contentManager; - } + public override async Task DisplayAsync(ContentItem model, BuildDisplayContext context) + { + var httpContext = _httpContextAccessor.HttpContext; + var dashboardFeature = httpContext.Features.Get(); - public override async Task DisplayAsync(ContentItem model, BuildDisplayContext context) + // Return if it's not Manage dashboard request + if (dashboardFeature == null || !dashboardFeature.IsManageRequest) { - var httpContext = _httpContextAccessor.HttpContext; - var dashboardFeature = httpContext.Features.Get(); + return null; + } - // Return if it's not Manage dashboard request - if (dashboardFeature == null || !dashboardFeature.IsManageRequest) - { - return null; - } + var results = new List(); + var hasPublished = await _contentManager.HasPublishedVersionAsync(model); + var hasDraft = model.HasDraft(); + var hasEditPermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.EditContent, model); + var hasDeletePermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.DeleteContent, model); + var hasPublishPermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.PublishContent, model); - var results = new List(); - var hasPublished = await _contentManager.HasPublishedVersionAsync(model); - var hasDraft = model.HasDraft(); - var hasEditPermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.EditContent, model); - var hasDeletePermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.DeleteContent, model); - var hasPublishPermission = await _authorizationService.AuthorizeAsync(httpContext.User, CommonPermissions.PublishContent, model); + var dragHandle = Initialize("Dashboard_DragHandle", m => + { + m.ContentItem = model; + }).Location("Leading:before"); + results.Add(dragHandle); - var dragHandle = Initialize("Dashboard_DragHandle", m => + if (hasEditPermission) + { + var editButton = Initialize("Dashboard_EditButton", m => { m.ContentItem = model; - }).Location("Leading:before"); - results.Add(dragHandle); - - if (hasEditPermission) - { - var editButton = Initialize("Dashboard_EditButton", m => - { - m.ContentItem = model; - }).Location("ActionsMenu:after"); - results.Add(editButton); - } - - if (hasDeletePermission) - { - var deleteButton = Initialize("Dashboard_DeleteButton", m => - { - m.ContentItem = model; - }).Location("ActionsMenu:after"); - results.Add(deleteButton); - } + }).Location("ActionsMenu:after"); + results.Add(editButton); + } - if (hasPublished && hasPublishPermission) + if (hasDeletePermission) + { + var deleteButton = Initialize("Dashboard_DeleteButton", m => { - var unpublishButton = Initialize("Dashboard_UnpublishButton", m => - { - m.ContentItem = model; - }).Location("ActionsMenu:after"); - results.Add(unpublishButton); - } + m.ContentItem = model; + }).Location("ActionsMenu:after"); + results.Add(deleteButton); + } - if (hasDraft && hasPublishPermission) + if (hasPublished && hasPublishPermission) + { + var unpublishButton = Initialize("Dashboard_UnpublishButton", m => { - var publishButton = Initialize("Dashboard_PublishButton", m => - { - m.ContentItem = model; - }).Location("ActionsMenu:after"); - results.Add(publishButton); - } + m.ContentItem = model; + }).Location("ActionsMenu:after"); + results.Add(unpublishButton); + } - if (hasDraft && hasEditPermission) + if (hasDraft && hasPublishPermission) + { + var publishButton = Initialize("Dashboard_PublishButton", m => { - var discardDraftButton = Initialize("Dashboard_DiscardDraftButton", m => - { - m.ContentItem = model; - }).Location("ActionsMenu:after"); - results.Add(discardDraftButton); - } + m.ContentItem = model; + }).Location("ActionsMenu:after"); + results.Add(publishButton); + } - var shapeTag = Initialize("DashboardWidget_DetailAdmin__ContentsTags", m => + if (hasDraft && hasEditPermission) + { + var discardDraftButton = Initialize("Dashboard_DiscardDraftButton", m => { m.ContentItem = model; - }).Location("DetailAdmin", "Tags:10"); - results.Add(shapeTag); - - return Combine(results.ToArray()); + }).Location("ActionsMenu:after"); + results.Add(discardDraftButton); } + + var shapeTag = Initialize("DashboardWidget_DetailAdmin__ContentsTags", m => + { + m.ContentItem = model; + }).Location("DetailAdmin", "Tags:10"); + results.Add(shapeTag); + + return Combine(results.ToArray()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardPartDisplayDriver.cs index 2d502ed7903..f105cb79fbd 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Drivers/DashboardPartDisplayDriver.cs @@ -6,37 +6,36 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AdminDashboard.Drivers +namespace OrchardCore.AdminDashboard.Drivers; + +public sealed class DashboardPartDisplayDriver : ContentPartDisplayDriver { - public sealed class DashboardPartDisplayDriver : ContentPartDisplayDriver + public override Task DisplayAsync(DashboardPart part, BuildPartDisplayContext context) { - public override Task DisplayAsync(DashboardPart part, BuildPartDisplayContext context) - { - return Task.FromResult(null); - } + return Task.FromResult(null); + } - public override IDisplayResult Edit(DashboardPart dashboardPart, BuildPartEditorContext context) - { - return Initialize(GetEditorShapeType(context), m => BuildViewModel(m, dashboardPart)); - } + public override IDisplayResult Edit(DashboardPart dashboardPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), m => BuildViewModel(m, dashboardPart)); + } - public override async Task UpdateAsync(DashboardPart model, UpdatePartEditorContext context) - { - await context.Updater.TryUpdateModelAsync(model, Prefix, - t => t.Position, - t => t.Width, - t => t.Height); + public override async Task UpdateAsync(DashboardPart model, UpdatePartEditorContext context) + { + await context.Updater.TryUpdateModelAsync(model, Prefix, + t => t.Position, + t => t.Width, + t => t.Height); - return Edit(model, context); - } + return Edit(model, context); + } - private static void BuildViewModel(DashboardPartViewModel model, DashboardPart part) - { - model.Position = part.Position; - model.Width = part.Width; - model.Height = part.Height; - model.DashboardPart = part; - model.ContentItem = part.ContentItem; - } + private static void BuildViewModel(DashboardPartViewModel model, DashboardPart part) + { + model.Position = part.Position; + model.Width = part.Width; + model.Height = part.Height; + model.DashboardPart = part; + model.ContentItem = part.ContentItem; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Indexes/DashboardPartIndex.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Indexes/DashboardPartIndex.cs index 4e70965687d..40927b0bc46 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Indexes/DashboardPartIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Indexes/DashboardPartIndex.cs @@ -2,38 +2,37 @@ using OrchardCore.ContentManagement; using YesSql.Indexes; -namespace OrchardCore.AdminDashboard.Indexes +namespace OrchardCore.AdminDashboard.Indexes; + +public class DashboardPartIndex : MapIndex { - public class DashboardPartIndex : MapIndex - { - public double Position { get; set; } - } + public double Position { get; set; } +} - public class DashboardPartIndexProvider : IndexProvider +public class DashboardPartIndexProvider : IndexProvider +{ + public override void Describe(DescribeContext context) { - public override void Describe(DescribeContext context) - { - context.For() - .Map(contentItem => - { - var dashboardPart = contentItem.As(); + context.For() + .Map(contentItem => + { + var dashboardPart = contentItem.As(); - // Store only published and latest versions - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + // Store only published and latest versions + if (!contentItem.Published && !contentItem.Latest) + { + return null; + } - if (dashboardPart != null) + if (dashboardPart != null) + { + return new DashboardPartIndex { - return new DashboardPartIndex - { - Position = dashboardPart.Position, - }; - } + Position = dashboardPart.Position, + }; + } - return null; - }); - } + return null; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Migrations.cs index e31897c300a..a7664223ea8 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Migrations.cs @@ -7,61 +7,60 @@ using OrchardCore.Recipes.Services; using YesSql.Sql; -namespace OrchardCore.AdminDashboard +namespace OrchardCore.AdminDashboard; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IRecipeMigrator _recipeMigrator; + + public Migrations( + IContentDefinitionManager contentDefinitionManager, + IRecipeMigrator recipeMigrator) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IRecipeMigrator _recipeMigrator; + _contentDefinitionManager = contentDefinitionManager; + _recipeMigrator = recipeMigrator; + } - public Migrations( - IContentDefinitionManager contentDefinitionManager, - IRecipeMigrator recipeMigrator) - { - _contentDefinitionManager = contentDefinitionManager; - _recipeMigrator = recipeMigrator; - } + public async Task CreateAsync() + { + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("Position") + ); - public async Task CreateAsync() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("Position") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DashboardPart_DocumentId", + "DocumentId", + "Position") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DashboardPart_DocumentId", - "DocumentId", - "Position") + await _contentDefinitionManager.AlterPartDefinitionAsync("DashboardPart", builder => builder + .Attachable() + .WithDescription("Provides a way to add widgets to a dashboard.") ); - await _contentDefinitionManager.AlterPartDefinitionAsync("DashboardPart", builder => builder - .Attachable() - .WithDescription("Provides a way to add widgets to a dashboard.") - ); - - await _recipeMigrator.ExecuteAsync($"dashboard-widgets{RecipesConstants.RecipeExtension}", this); + await _recipeMigrator.ExecuteAsync($"dashboard-widgets{RecipesConstants.RecipeExtension}", this); - // Shortcut other migration steps on new content definition schemas. - return 3; - } + // Shortcut other migration steps on new content definition schemas. + return 3; + } - public async Task UpdateFrom1Async() - { - await _recipeMigrator.ExecuteAsync($"dashboard-widgets{RecipesConstants.RecipeExtension}", this); + public async Task UpdateFrom1Async() + { + await _recipeMigrator.ExecuteAsync($"dashboard-widgets{RecipesConstants.RecipeExtension}", this); - return 2; - } + return 2; + } - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DashboardPart_DocumentId", - "DocumentId", - "Position") - ); + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DashboardPart_DocumentId", + "DocumentId", + "Position") + ); - return 3; - } + return 3; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Models/DashboardPart.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Models/DashboardPart.cs index 8403922180d..721a5115692 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Models/DashboardPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Models/DashboardPart.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.AdminDashboard.Models +namespace OrchardCore.AdminDashboard.Models; + +public class DashboardPart : ContentPart { - public class DashboardPart : ContentPart - { - public double Position { get; set; } - public double Width { get; set; } = 1.0; - public double Height { get; set; } = 1.0; - } + public double Position { get; set; } + public double Width { get; set; } = 1.0; + public double Height { get; set; } = 1.0; } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/AdminDashboardService.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/AdminDashboardService.cs index f9d03c02c90..188c55d608a 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/AdminDashboardService.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/AdminDashboardService.cs @@ -7,26 +7,25 @@ using OrchardCore.ContentManagement.Records; using YesSql; -namespace OrchardCore.AdminDashboard.Services +namespace OrchardCore.AdminDashboard.Services; + +public class AdminDashboardService : IAdminDashboardService { - public class AdminDashboardService : IAdminDashboardService - { - private readonly ISession _session; + private readonly ISession _session; - public AdminDashboardService(ISession session) - { - _session = session; - } + public AdminDashboardService(ISession session) + { + _session = session; + } - public async Task> GetWidgetsAsync(Expression> predicate) - { - var widgets = await _session - .Query() - .OrderBy(w => w.Position) - .With(predicate) - .ListAsync(); + public async Task> GetWidgetsAsync(Expression> predicate) + { + var widgets = await _session + .Query() + .OrderBy(w => w.Position) + .With(predicate) + .ListAsync(); - return widgets; - } + return widgets; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/IAdminDashboardService.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/IAdminDashboardService.cs index bfac84c58e6..35ec639a5d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/IAdminDashboardService.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Services/IAdminDashboardService.cs @@ -5,13 +5,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Records; -namespace OrchardCore.AdminDashboard.Services +namespace OrchardCore.AdminDashboard.Services; + +/// +/// Provides services to manage the Admin Dashboards. +/// +public interface IAdminDashboardService { - /// - /// Provides services to manage the Admin Dashboards. - /// - public interface IAdminDashboardService - { - Task> GetWidgetsAsync(Expression> predicate); - } + Task> GetWidgetsAsync(Expression> predicate); } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Startup.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Startup.cs index 1881c834fb2..8073e6c7844 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/Startup.cs @@ -17,45 +17,44 @@ using OrchardCore.Mvc.Core.Utilities; using OrchardCore.Security.Permissions; -namespace OrchardCore.AdminDashboard -{ - public sealed class Startup : StartupBase - { - public override int ConfigureOrder => -10; +namespace OrchardCore.AdminDashboard; - private readonly AdminOptions _adminOptions; +public sealed class Startup : StartupBase +{ + public override int ConfigureOrder => -10; - public Startup(IOptions adminOptions) - { - _adminOptions = adminOptions.Value; - } + private readonly AdminOptions _adminOptions; - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); + public Startup(IOptions adminOptions) + { + _adminOptions = adminOptions.Value; + } - services.AddScoped(); - services.AddIndexProvider(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddScoped(); + services.AddIndexProvider(); - services.AddScoped(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddDataMigration(); - } + services.AddScoped(); - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // Dashboard - var dashboardControllerName = typeof(DashboardController).ControllerName(); + services.AddDataMigration(); + } - routes.MapAreaControllerRoute( - name: "AdminDashboard", - areaName: "OrchardCore.AdminDashboard", - pattern: _adminOptions.AdminUrlPrefix, - defaults: new { controller = dashboardControllerName, action = nameof(DashboardController.Index) } - ); - } + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // Dashboard + var dashboardControllerName = typeof(DashboardController).ControllerName(); + + routes.MapAreaControllerRoute( + name: "AdminDashboard", + areaName: "OrchardCore.AdminDashboard", + pattern: _adminOptions.AdminUrlPrefix, + defaults: new { controller = dashboardControllerName, action = nameof(DashboardController.Index) } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/AdminDashboardViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/AdminDashboardViewModel.cs index 7805a8ec978..9be1b519701 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/AdminDashboardViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/AdminDashboardViewModel.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.AdminDashboard.ViewModels +namespace OrchardCore.AdminDashboard.ViewModels; + +public class AdminDashboardViewModel { - public class AdminDashboardViewModel - { - [BindNever] - public bool CanManageDashboard { get; set; } + [BindNever] + public bool CanManageDashboard { get; set; } - [BindNever] - public DashboardWrapper[] Dashboards { get; set; } + [BindNever] + public DashboardWrapper[] Dashboards { get; set; } - [BindNever] - public List Creatable { get; set; } - } + [BindNever] + public List Creatable { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardPartViewModel.cs index 6a11b04c0ff..095394cb00d 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardPartViewModel.cs @@ -2,19 +2,18 @@ using OrchardCore.AdminDashboard.Models; using OrchardCore.ContentManagement; -namespace OrchardCore.AdminDashboard.ViewModels +namespace OrchardCore.AdminDashboard.ViewModels; + +public class DashboardPartViewModel { - public class DashboardPartViewModel - { - public string ContentItemId { get; set; } - public double Position { get; set; } - public double Width { get; set; } - public double Height { get; set; } + public string ContentItemId { get; set; } + public double Position { get; set; } + public double Width { get; set; } + public double Height { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public DashboardPart DashboardPart { get; set; } - } + [BindNever] + public DashboardPart DashboardPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardWrapper.cs b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardWrapper.cs index cf293deeb7b..f08533c0c64 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardWrapper.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminDashboard/ViewModels/DashboardWrapper.cs @@ -2,16 +2,14 @@ using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AdminDashboard.ViewModels +namespace OrchardCore.AdminDashboard.ViewModels; + +public class DashboardWrapper : ShapeViewModel { - public class DashboardWrapper : ShapeViewModel + public DashboardWrapper() : base("Dashboard_Wrapper") { - public DashboardWrapper() : base("Dashboard_Wrapper") - { - } - - public ContentItem Dashboard { get; set; } - public IShape Content { get; set; } } + public ContentItem Dashboard { get; set; } + public IShape Content { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminMenu.cs index 01196918b0d..76ad7d16913 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminMenu.cs @@ -3,40 +3,39 @@ using OrchardCore.AdminMenu.Services; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu +namespace OrchardCore.AdminMenu; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider - { - private readonly AdminMenuNavigationProvidersCoordinator _adminMenuNavigationProvider; + private readonly AdminMenuNavigationProvidersCoordinator _adminMenuNavigationProvider; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(AdminMenuNavigationProvidersCoordinator adminMenuNavigationProvider, - IStringLocalizer localizer) - { - _adminMenuNavigationProvider = adminMenuNavigationProvider; - S = localizer; - } + public AdminMenu(AdminMenuNavigationProvidersCoordinator adminMenuNavigationProvider, + IStringLocalizer localizer) + { + _adminMenuNavigationProvider = adminMenuNavigationProvider; + S = localizer; + } - public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return; - } + return; + } - // Configuration and settings menus for the AdminMenu module - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Admin Menus"], S["Admin Menus"].PrefixPosition(), adminMenu => adminMenu - .Permission(Permissions.ManageAdminMenu) - .Action("List", "Menu", "OrchardCore.AdminMenu") - .LocalNav() - ) - ); + // Configuration and settings menus for the AdminMenu module + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Admin Menus"], S["Admin Menus"].PrefixPosition(), adminMenu => adminMenu + .Permission(Permissions.ManageAdminMenu) + .Action("List", "Menu", "OrchardCore.AdminMenu") + .LocalNav() + ) + ); - // This is the entry point for the adminMenu: dynamically generated custom admin menus - await _adminMenuNavigationProvider.BuildNavigationAsync(NavigationConstants.AdminMenuId, builder); - } + // This is the entry point for the adminMenu: dynamically generated custom admin menus + await _adminMenuNavigationProvider.BuildNavigationAsync(NavigationConstants.AdminMenuId, builder); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNode.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNode.cs index 6c522d97827..be4788e73bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNode.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; using OrchardCore.AdminMenu.Models; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class LinkAdminNode : AdminNode { - public class LinkAdminNode : AdminNode - { - [Required] - public string LinkText { get; set; } + [Required] + public string LinkText { get; set; } - [Required] - public string LinkUrl { get; set; } + [Required] + public string LinkUrl { get; set; } - public string IconClass { get; set; } + public string IconClass { get; set; } - /// - /// The names of the permissions required to view this admin menu node. - /// - public string[] PermissionNames { get; set; } = []; - } + /// + /// The names of the permissions required to view this admin menu node. + /// + public string[] PermissionNames { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs index b0068519ac1..5690f12d5b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs @@ -6,78 +6,77 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public sealed class LinkAdminNodeDriver : DisplayDriver { - public sealed class LinkAdminNodeDriver : DisplayDriver - { - private readonly IAdminMenuPermissionService _adminMenuPermissionService; + private readonly IAdminMenuPermissionService _adminMenuPermissionService; - public LinkAdminNodeDriver(IAdminMenuPermissionService adminMenuPermissionService) - { - _adminMenuPermissionService = adminMenuPermissionService; - } + public LinkAdminNodeDriver(IAdminMenuPermissionService adminMenuPermissionService) + { + _adminMenuPermissionService = adminMenuPermissionService; + } - public override Task DisplayAsync(LinkAdminNode treeNode, BuildDisplayContext context) - { - return CombineAsync( - View("LinkAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), - View("LinkAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") - ); - } + public override Task DisplayAsync(LinkAdminNode treeNode, BuildDisplayContext context) + { + return CombineAsync( + View("LinkAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), + View("LinkAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") + ); + } - public override IDisplayResult Edit(LinkAdminNode treeNode, BuildEditorContext context) + public override IDisplayResult Edit(LinkAdminNode treeNode, BuildEditorContext context) + { + return Initialize("LinkAdminNode_Fields_TreeEdit", async model => { - return Initialize("LinkAdminNode_Fields_TreeEdit", async model => - { - model.LinkText = treeNode.LinkText; - model.LinkUrl = treeNode.LinkUrl; - model.IconClass = treeNode.IconClass; - model.Target = treeNode.Target; + model.LinkText = treeNode.LinkText; + model.LinkUrl = treeNode.LinkUrl; + model.IconClass = treeNode.IconClass; + model.Target = treeNode.Target; - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name)); + var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name)); - model.SelectedItems = selectedPermissions - .Select(p => new PermissionViewModel - { - Name = p.Name, - DisplayText = p.Description - }).ToList(); - model.AllItems = permissions - .Select(p => new PermissionViewModel - { - Name = p.Name, - DisplayText = p.Description - }).ToList(); - }).Location("Content"); - } + model.SelectedItems = selectedPermissions + .Select(p => new PermissionViewModel + { + Name = p.Name, + DisplayText = p.Description + }).ToList(); + model.AllItems = permissions + .Select(p => new PermissionViewModel + { + Name = p.Name, + DisplayText = p.Description + }).ToList(); + }).Location("Content"); + } - public override async Task UpdateAsync(LinkAdminNode treeNode, UpdateEditorContext context) - { - var model = new LinkAdminNodeViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - x => x.LinkUrl, - x => x.LinkText, - x => x.Target, - x => x.IconClass, - x => x.SelectedPermissionNames); + public override async Task UpdateAsync(LinkAdminNode treeNode, UpdateEditorContext context) + { + var model = new LinkAdminNodeViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, + x => x.LinkUrl, + x => x.LinkText, + x => x.Target, + x => x.IconClass, + x => x.SelectedPermissionNames); - treeNode.LinkText = model.LinkText; - treeNode.LinkUrl = model.LinkUrl; - treeNode.Target = model.Target; - treeNode.IconClass = model.IconClass; + treeNode.LinkText = model.LinkText; + treeNode.LinkUrl = model.LinkUrl; + treeNode.Target = model.Target; + treeNode.IconClass = model.IconClass; - var selectedPermissions = model.SelectedPermissionNames == null - ? [] - : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries); + var selectedPermissions = model.SelectedPermissionNames == null + ? [] + : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries); - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - treeNode.PermissionNames = permissions - .Where(p => selectedPermissions.Contains(p.Name)) - .Select(p => p.Name).ToArray(); + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + treeNode.PermissionNames = permissions + .Where(p => selectedPermissions.Contains(p.Name)) + .Select(p => p.Name).ToArray(); - return Edit(treeNode, context); - } + return Edit(treeNode, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs index b8f8d3035e6..a7806298fcd 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs @@ -9,88 +9,87 @@ using OrchardCore.AdminMenu.Services; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class LinkAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder { - public class LinkAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder + private readonly ILogger _logger; + private readonly IAdminMenuPermissionService _adminMenuPermissionService; + private readonly AdminOptions _adminOptions; + + + public LinkAdminNodeNavigationBuilder( + IAdminMenuPermissionService adminMenuPermissionService, + IOptions adminOptions, + ILogger logger) { - private readonly ILogger _logger; - private readonly IAdminMenuPermissionService _adminMenuPermissionService; - private readonly AdminOptions _adminOptions; + _adminMenuPermissionService = adminMenuPermissionService; + _adminOptions = adminOptions.Value; + _logger = logger; + } + public string Name => nameof(LinkAdminNode); - public LinkAdminNodeNavigationBuilder( - IAdminMenuPermissionService adminMenuPermissionService, - IOptions adminOptions, - ILogger logger) + public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + { + var node = menuItem as LinkAdminNode; + if (node == null || string.IsNullOrEmpty(node.LinkText) || !node.Enabled) { - _adminMenuPermissionService = adminMenuPermissionService; - _adminOptions = adminOptions.Value; - _logger = logger; + return Task.CompletedTask; } - public string Name => nameof(LinkAdminNode); - - public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder => { - var node = menuItem as LinkAdminNode; - if (node == null || string.IsNullOrEmpty(node.LinkText) || !node.Enabled) + var nodeLinkUrl = node.LinkUrl; + if (!string.IsNullOrEmpty(nodeLinkUrl) && nodeLinkUrl[0] != '/' && !nodeLinkUrl.Contains("://")) { - return Task.CompletedTask; - } - - return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder => - { - var nodeLinkUrl = node.LinkUrl; - if (!string.IsNullOrEmpty(nodeLinkUrl) && nodeLinkUrl[0] != '/' && !nodeLinkUrl.Contains("://")) + if (nodeLinkUrl.StartsWith("~/", StringComparison.Ordinal)) { - if (nodeLinkUrl.StartsWith("~/", StringComparison.Ordinal)) - { - nodeLinkUrl = nodeLinkUrl[2..]; - } + nodeLinkUrl = nodeLinkUrl[2..]; + } - // Check if the first segment of 'nodeLinkUrl' is not equal to the admin prefix. - if (!nodeLinkUrl.StartsWith($"{_adminOptions.AdminUrlPrefix}", StringComparison.OrdinalIgnoreCase) || - (nodeLinkUrl.Length != _adminOptions.AdminUrlPrefix.Length - && nodeLinkUrl[_adminOptions.AdminUrlPrefix.Length] != '/')) - { - nodeLinkUrl = $"{_adminOptions.AdminUrlPrefix}/{nodeLinkUrl}"; - } + // Check if the first segment of 'nodeLinkUrl' is not equal to the admin prefix. + if (!nodeLinkUrl.StartsWith($"{_adminOptions.AdminUrlPrefix}", StringComparison.OrdinalIgnoreCase) || + (nodeLinkUrl.Length != _adminOptions.AdminUrlPrefix.Length + && nodeLinkUrl[_adminOptions.AdminUrlPrefix.Length] != '/')) + { + nodeLinkUrl = $"{_adminOptions.AdminUrlPrefix}/{nodeLinkUrl}"; } + } - // Add the actual link. - itemBuilder.Url(nodeLinkUrl); - itemBuilder.Target(node.Target); - itemBuilder.Priority(node.Priority); - itemBuilder.Position(node.Position); + // Add the actual link. + itemBuilder.Url(nodeLinkUrl); + itemBuilder.Target(node.Target); + itemBuilder.Priority(node.Priority); + itemBuilder.Position(node.Position); - if (node.PermissionNames.Length > 0) - { - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + if (node.PermissionNames.Length > 0) + { + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - // Find the actual permissions and apply them to the menu. - var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name)); - itemBuilder.Permissions(selectedPermissions); - } + // Find the actual permissions and apply them to the menu. + var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name)); + itemBuilder.Permissions(selectedPermissions); + } - // Add adminNode's IconClass property values to menuItem.Classes. - // Add them with a prefix so that later the shape template can extract them to use them on a tag. - node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c)); + // Add adminNode's IconClass property values to menuItem.Classes. + // Add them with a prefix so that later the shape template can extract them to use them on a tag. + node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c)); - // Let children build themselves inside this MenuItem. - // Todo: This logic can be shared by all TreeNodeNavigationBuilders. - foreach (var childTreeNode in menuItem.Items) + // Let children build themselves inside this MenuItem. + // Todo: This logic can be shared by all TreeNodeNavigationBuilders. + foreach (var childTreeNode in menuItem.Items) + { + try { - try - { - var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name); - await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name); - } + var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name); + await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders); } - }); - } + catch (Exception e) + { + _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name); + } + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs index b2335ee58e7..31ca1efc603 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs @@ -2,26 +2,25 @@ using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class LinkAdminNodeViewModel { - public class LinkAdminNodeViewModel - { - [Required] - public string LinkText { get; set; } + [Required] + public string LinkText { get; set; } - [Required] - public string LinkUrl { get; set; } + [Required] + public string LinkUrl { get; set; } - public string Target { get; set; } + public string Target { get; set; } - public string IconClass { get; set; } + public string IconClass { get; set; } - public string SelectedPermissionNames { get; set; } + public string SelectedPermissionNames { get; set; } - [BindNever] - public IList SelectedItems { get; set; } + [BindNever] + public IList SelectedItems { get; set; } - [BindNever] - public IList AllItems { get; set; } - } + [BindNever] + public IList AllItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PermissionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PermissionViewModel.cs index 306c75f373b..73ed1b3cc90 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PermissionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PermissionViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class PermissionViewModel { - public class PermissionViewModel - { - public string Name { get; set; } - public string DisplayText { get; set; } - } + public string Name { get; set; } + public string DisplayText { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNode.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNode.cs index afa2d8d07cd..fd829667feb 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNode.cs @@ -1,18 +1,17 @@ using System.ComponentModel.DataAnnotations; using OrchardCore.AdminMenu.Models; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class PlaceholderAdminNode : AdminNode { - public class PlaceholderAdminNode : AdminNode - { - [Required] - public string LinkText { get; set; } + [Required] + public string LinkText { get; set; } - public string IconClass { get; set; } + public string IconClass { get; set; } - /// - /// The names of the permissions required to view this admin menu node. - /// - public string[] PermissionNames { get; set; } = []; - } + /// + /// The names of the permissions required to view this admin menu node. + /// + public string[] PermissionNames { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeDriver.cs index a650ee28582..f523c5449b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeDriver.cs @@ -6,69 +6,68 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public sealed class PlaceholderAdminNodeDriver : DisplayDriver { - public sealed class PlaceholderAdminNodeDriver : DisplayDriver - { - private readonly IAdminMenuPermissionService _adminMenuPermissionService; + private readonly IAdminMenuPermissionService _adminMenuPermissionService; - public PlaceholderAdminNodeDriver(IAdminMenuPermissionService adminMenuPermissionService) - { - _adminMenuPermissionService = adminMenuPermissionService; - } + public PlaceholderAdminNodeDriver(IAdminMenuPermissionService adminMenuPermissionService) + { + _adminMenuPermissionService = adminMenuPermissionService; + } - public override Task DisplayAsync(PlaceholderAdminNode treeNode, BuildDisplayContext context) - { - return CombineAsync( - View("PlaceholderAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), - View("PlaceholderAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") - ); - } + public override Task DisplayAsync(PlaceholderAdminNode treeNode, BuildDisplayContext context) + { + return CombineAsync( + View("PlaceholderAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), + View("PlaceholderAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") + ); + } - public override IDisplayResult Edit(PlaceholderAdminNode treeNode, BuildEditorContext context) + public override IDisplayResult Edit(PlaceholderAdminNode treeNode, BuildEditorContext context) + { + return Initialize("PlaceholderAdminNode_Fields_TreeEdit", async model => { - return Initialize("PlaceholderAdminNode_Fields_TreeEdit", async model => - { - model.LinkText = treeNode.LinkText; - model.IconClass = treeNode.IconClass; + model.LinkText = treeNode.LinkText; + model.IconClass = treeNode.IconClass; - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name)); + var selectedPermissions = permissions.Where(p => treeNode.PermissionNames.Contains(p.Name)); - model.SelectedItems = selectedPermissions - .Select(p => new PermissionViewModel - { - Name = p.Name, - DisplayText = p.Description - }).ToList(); - model.AllItems = permissions - .Select(p => new PermissionViewModel - { - Name = p.Name, - DisplayText = p.Description - }).ToList(); - }).Location("Content"); - } + model.SelectedItems = selectedPermissions + .Select(p => new PermissionViewModel + { + Name = p.Name, + DisplayText = p.Description + }).ToList(); + model.AllItems = permissions + .Select(p => new PermissionViewModel + { + Name = p.Name, + DisplayText = p.Description + }).ToList(); + }).Location("Content"); + } - public override async Task UpdateAsync(PlaceholderAdminNode treeNode, UpdateEditorContext context) - { - var model = new PlaceholderAdminNodeViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - x => x.LinkText, - x => x.IconClass, - x => x.SelectedPermissionNames); + public override async Task UpdateAsync(PlaceholderAdminNode treeNode, UpdateEditorContext context) + { + var model = new PlaceholderAdminNodeViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, + x => x.LinkText, + x => x.IconClass, + x => x.SelectedPermissionNames); - treeNode.LinkText = model.LinkText; - treeNode.IconClass = model.IconClass; + treeNode.LinkText = model.LinkText; + treeNode.IconClass = model.IconClass; - var selectedPermissions = (model.SelectedPermissionNames == null ? Array.Empty() : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries)); - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - treeNode.PermissionNames = permissions - .Where(p => selectedPermissions.Contains(p.Name)) - .Select(p => p.Name).ToArray(); + var selectedPermissions = (model.SelectedPermissionNames == null ? Array.Empty() : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries)); + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + treeNode.PermissionNames = permissions + .Where(p => selectedPermissions.Contains(p.Name)) + .Select(p => p.Name).ToArray(); - return Edit(treeNode, context); - } + return Edit(treeNode, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeNavigationBuilder.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeNavigationBuilder.cs index d3128008e47..1ad01a4046d 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeNavigationBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeNavigationBuilder.cs @@ -7,62 +7,61 @@ using OrchardCore.AdminMenu.Services; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class PlaceholderAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder { - public class PlaceholderAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder + private readonly ILogger _logger; + private readonly IAdminMenuPermissionService _adminMenuPermissionService; + + public PlaceholderAdminNodeNavigationBuilder(IAdminMenuPermissionService adminMenuPermissionService, ILogger logger) { - private readonly ILogger _logger; - private readonly IAdminMenuPermissionService _adminMenuPermissionService; + _adminMenuPermissionService = adminMenuPermissionService; + _logger = logger; + } + + public string Name => nameof(PlaceholderAdminNode); - public PlaceholderAdminNodeNavigationBuilder(IAdminMenuPermissionService adminMenuPermissionService, ILogger logger) + public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + { + var node = menuItem as PlaceholderAdminNode; + + if (node == null || string.IsNullOrEmpty(node.LinkText) || !node.Enabled) { - _adminMenuPermissionService = adminMenuPermissionService; - _logger = logger; + return Task.CompletedTask; } - public string Name => nameof(PlaceholderAdminNode); - - public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder => { - var node = menuItem as PlaceholderAdminNode; + itemBuilder.Priority(node.Priority); + itemBuilder.Position(node.Position); - if (node == null || string.IsNullOrEmpty(node.LinkText) || !node.Enabled) + if (node.PermissionNames.Length > 0) { - return Task.CompletedTask; + var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); + // Find the actual permissions and apply them to the menu. + var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name)); + itemBuilder.Permissions(selectedPermissions); } - return builder.AddAsync(new LocalizedString(node.LinkText, node.LinkText), async itemBuilder => - { - itemBuilder.Priority(node.Priority); - itemBuilder.Position(node.Position); + // Add adminNode's IconClass property values to menuItem.Classes. + // Add them with a prefix so that later the shape template can extract them to use them on a tag. + node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c)); - if (node.PermissionNames.Length > 0) + // Let children build themselves inside this MenuItem + // todo: this logic can be shared by all TreeNodeNavigationBuilders + foreach (var childTreeNode in menuItem.Items) + { + try { - var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); - // Find the actual permissions and apply them to the menu. - var selectedPermissions = permissions.Where(p => node.PermissionNames.Contains(p.Name)); - itemBuilder.Permissions(selectedPermissions); + var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name); + await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders); } - - // Add adminNode's IconClass property values to menuItem.Classes. - // Add them with a prefix so that later the shape template can extract them to use them on a tag. - node.IconClass?.Split(' ').ToList().ForEach(c => itemBuilder.AddClass("icon-class-" + c)); - - // Let children build themselves inside this MenuItem - // todo: this logic can be shared by all TreeNodeNavigationBuilders - foreach (var childTreeNode in menuItem.Items) + catch (Exception e) { - try - { - var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childTreeNode.GetType().Name); - await treeBuilder.BuildNavigationAsync(childTreeNode, itemBuilder, treeNodeBuilders); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name); - } + _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childTreeNode.GetType().Name); } - }); - } + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeViewModel.cs index 4a380af3212..3224d92824d 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/PlaceholderAdminNodeViewModel.cs @@ -2,21 +2,20 @@ using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.AdminMenu.AdminNodes +namespace OrchardCore.AdminMenu.AdminNodes; + +public class PlaceholderAdminNodeViewModel { - public class PlaceholderAdminNodeViewModel - { - [Required] - public string LinkText { get; set; } + [Required] + public string LinkText { get; set; } - public string IconClass { get; set; } + public string IconClass { get; set; } - public string SelectedPermissionNames { get; set; } + public string SelectedPermissionNames { get; set; } - [BindNever] - public IList SelectedItems { get; set; } + [BindNever] + public IList SelectedItems { get; set; } - [BindNever] - public IList AllItems { get; set; } - } + [BindNever] + public IList AllItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/MenuController.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/MenuController.cs index 639adf36476..60707c3fbbc 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/MenuController.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/MenuController.cs @@ -18,284 +18,283 @@ using OrchardCore.Navigation; using OrchardCore.Routing; -namespace OrchardCore.AdminMenu.Controllers +namespace OrchardCore.AdminMenu.Controllers; + +[Admin("AdminMenu/{action}/{id?}", "AdminMenu{action}")] +public class MenuController : Controller { - [Admin("AdminMenu/{action}/{id?}", "AdminMenu{action}")] - public class MenuController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly IAdminMenuService _adminMenuService; + private readonly PagerOptions _pagerOptions; + private readonly IShapeFactory _shapeFactory; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public MenuController( + IAuthorizationService authorizationService, + IAdminMenuService adminMenuService, + IOptions pagerOptions, + IShapeFactory shapeFactory, + INotifier notifier, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + ILogger logger) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly IAdminMenuService _adminMenuService; - private readonly PagerOptions _pagerOptions; - private readonly IShapeFactory _shapeFactory; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public MenuController( - IAuthorizationService authorizationService, - IAdminMenuService adminMenuService, - IOptions pagerOptions, - IShapeFactory shapeFactory, - INotifier notifier, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - ILogger logger) - { - _authorizationService = authorizationService; - _adminMenuService = adminMenuService; - _pagerOptions = pagerOptions.Value; - _shapeFactory = shapeFactory; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; - _logger = logger; - } + _authorizationService = authorizationService; + _adminMenuService = adminMenuService; + _pagerOptions = pagerOptions.Value; + _shapeFactory = shapeFactory; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + _logger = logger; + } - public async Task List(ContentOptions options, PagerParameters pagerParameters) + public async Task List(ContentOptions options, PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } - - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + return Forbid(); + } - var adminMenuList = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu; + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - adminMenuList = adminMenuList.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + var adminMenuList = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu; - var startIndex = pager.GetStartIndex(); - var pageSize = pager.PageSize; - IEnumerable results = []; - - // todo: handle the case where there is a deserialization exception on some of the presets. - // load at least the ones without error. Provide a way to delete the ones on error. - try - { - results = adminMenuList - .Skip(startIndex) - .Take(pageSize) - .ToList(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error when retrieving the list of admin menus."); - await _notifier.ErrorAsync(H["Error when retrieving the list of admin menus."]); - } - - // Maintain previous route data when generating page links. - var routeData = new RouteData(); - - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + if (!string.IsNullOrWhiteSpace(options.Search)) + { + adminMenuList = adminMenuList.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - var pagerShape = await _shapeFactory.PagerAsync(pager, adminMenuList.Count, routeData); + var startIndex = pager.GetStartIndex(); + var pageSize = pager.PageSize; + IEnumerable results = []; - var model = new AdminMenuListViewModel - { - AdminMenu = results.Select(x => new AdminMenuEntry { AdminMenu = x }).ToList(), - Options = options, - Pager = pagerShape, - }; + // todo: handle the case where there is a deserialization exception on some of the presets. + // load at least the ones without error. Provide a way to delete the ones on error. + try + { + results = adminMenuList + .Skip(startIndex) + .Take(pageSize) + .ToList(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error when retrieving the list of admin menus."); + await _notifier.ErrorAsync(H["Error when retrieving the list of admin menus."]); + } - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - return View(model); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); } - [HttpPost, ActionName(nameof(List))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(AdminMenuListViewModel model) - => RedirectToAction(nameof(List), new RouteValueDictionary - { - {_optionsSearch, model.Options.Search } - }); + var pagerShape = await _shapeFactory.PagerAsync(pager, adminMenuList.Count, routeData); - public async Task Create() + var model = new AdminMenuListViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + AdminMenu = results.Select(x => new AdminMenuEntry { AdminMenu = x }).ToList(), + Options = options, + Pager = pagerShape, + }; - var model = new AdminMenuCreateViewModel(); + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Create(AdminMenuCreateViewModel model) + [HttpPost, ActionName(nameof(List))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(AdminMenuListViewModel model) + => RedirectToAction(nameof(List), new RouteValueDictionary { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + {_optionsSearch, model.Options.Search } + }); - if (ModelState.IsValid) - { - var tree = new Models.AdminMenu { Name = model.Name }; + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - await _adminMenuService.SaveAsync(tree); + var model = new AdminMenuCreateViewModel(); - return RedirectToAction(nameof(List)); - } + return View(model); + } - return View(model); + [HttpPost] + public async Task Create(AdminMenuCreateViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); } - public async Task Edit(string id) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + var tree = new Models.AdminMenu { Name = model.Name }; - var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - - if (adminMenu == null) - { - return NotFound(); - } - - var model = new AdminMenuEditViewModel - { - Id = adminMenu.Id, - Name = adminMenu.Name - }; + await _adminMenuService.SaveAsync(tree); - return View(model); + return RedirectToAction(nameof(List)); } - [HttpPost] - public async Task Edit(AdminMenuEditViewModel model) + return View(model); + } + + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.Id); + var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - if (adminMenu == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } - if (ModelState.IsValid) - { - adminMenu.Name = model.Name; + var model = new AdminMenuEditViewModel + { + Id = adminMenu.Id, + Name = adminMenu.Name + }; - await _adminMenuService.SaveAsync(adminMenu); + return View(model); + } - await _notifier.SuccessAsync(H["Admin menu updated successfully."]); + [HttpPost] + public async Task Edit(AdminMenuEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - return RedirectToAction(nameof(List)); - } + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.Id); - return View(model); + if (adminMenu == null) + { + return NotFound(); } - [HttpPost] - public async Task Delete(string id) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } - - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + adminMenu.Name = model.Name; - if (adminMenu == null) - { - await _notifier.ErrorAsync(H["Can't find the admin menu."]); - return RedirectToAction(nameof(List)); - } - - var removed = await _adminMenuService.DeleteAsync(adminMenu); + await _adminMenuService.SaveAsync(adminMenu); - if (removed == 1) - { - await _notifier.SuccessAsync(H["Admin menu deleted successfully."]); - } - else - { - await _notifier.ErrorAsync(H["Can't delete the admin menu."]); - } + await _notifier.SuccessAsync(H["Admin menu updated successfully."]); return RedirectToAction(nameof(List)); } - [HttpPost, ActionName(nameof(List))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + return View(model); + } + + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - if (itemIds?.Count() > 0) - { - var adminMenuList = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu; - var checkedContentItems = adminMenuList.Where(x => itemIds.Contains(x.Id)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - var adminMenu = adminMenuList.FirstOrDefault(x => string.Equals(x.Id, item.Id, StringComparison.OrdinalIgnoreCase)); - await _adminMenuService.DeleteAsync(adminMenu); - } - await _notifier.SuccessAsync(H["Admin menus successfully removed."]); - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } - } + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + if (adminMenu == null) + { + await _notifier.ErrorAsync(H["Can't find the admin menu."]); return RedirectToAction(nameof(List)); } - [HttpPost] - public async Task Toggle(string id) + var removed = await _adminMenuService.DeleteAsync(adminMenu); + + if (removed == 1) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + await _notifier.SuccessAsync(H["Admin menu deleted successfully."]); + } + else + { + await _notifier.ErrorAsync(H["Can't delete the admin menu."]); + } + + return RedirectToAction(nameof(List)); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + [HttpPost, ActionName(nameof(List))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - if (adminMenu == null) + if (itemIds?.Count() > 0) + { + var adminMenuList = (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu; + var checkedContentItems = adminMenuList.Where(x => itemIds.Contains(x.Id)); + switch (options.BulkAction) { - return NotFound(); + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + var adminMenu = adminMenuList.FirstOrDefault(x => string.Equals(x.Id, item.Id, StringComparison.OrdinalIgnoreCase)); + await _adminMenuService.DeleteAsync(adminMenu); + } + await _notifier.SuccessAsync(H["Admin menus successfully removed."]); + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } + } - adminMenu.Enabled = !adminMenu.Enabled; + return RedirectToAction(nameof(List)); + } - await _adminMenuService.SaveAsync(adminMenu); + [HttpPost] + public async Task Toggle(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - await _notifier.SuccessAsync(H["Admin menu toggled successfully."]); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - return RedirectToAction(nameof(List)); + if (adminMenu == null) + { + return NotFound(); } + + adminMenu.Enabled = !adminMenu.Enabled; + + await _adminMenuService.SaveAsync(adminMenu); + + await _notifier.SuccessAsync(H["Admin menu toggled successfully."]); + + return RedirectToAction(nameof(List)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/NodeController.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/NodeController.cs index 56567072107..a7990a7b9f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/NodeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Controllers/NodeController.cs @@ -12,337 +12,336 @@ using OrchardCore.DisplayManagement.Notify; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.Controllers +namespace OrchardCore.AdminMenu.Controllers; + +[Admin("AdminMenu/Node/{action}", "AdminMenuNode{action}")] +public class NodeController : Controller { - [Admin("AdminMenu/Node/{action}", "AdminMenuNode{action}")] - public class NodeController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _factories; + private readonly IAdminMenuService _adminMenuService; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + private readonly IUpdateModelAccessor _updateModelAccessor; + + + public NodeController( + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable factories, + IAdminMenuService adminMenuService, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IUpdateModelAccessor updateModelAccessor) { - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _factories; - private readonly IAdminMenuService _adminMenuService; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - private readonly IUpdateModelAccessor _updateModelAccessor; - - - public NodeController( - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable factories, - IAdminMenuService adminMenuService, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IUpdateModelAccessor updateModelAccessor) - { - _displayManager = displayManager; - _factories = factories; - _adminMenuService = adminMenuService; - _authorizationService = authorizationService; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - H = htmlLocalizer; - } + _displayManager = displayManager; + _factories = factories; + _adminMenuService = adminMenuService; + _authorizationService = authorizationService; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + H = htmlLocalizer; + } - public async Task List(string id) + public async Task List(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - if (adminMenu == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } + + return View(await BuildDisplayViewModel(adminMenu)); + } - return View(await BuildDisplayViewModel(adminMenu)); + private async Task BuildDisplayViewModel(Models.AdminMenu tree) + { + var thumbnails = new Dictionary(); + foreach (var factory in _factories) + { + var treeNode = factory.Create(); + var thumbnail = await _displayManager.BuildDisplayAsync(treeNode, _updateModelAccessor.ModelUpdater, "TreeThumbnail"); + thumbnail.Properties["TreeNode"] = treeNode; + thumbnails.Add(factory.Name, thumbnail); } - private async Task BuildDisplayViewModel(Models.AdminMenu tree) + var model = new AdminNodeListViewModel { - var thumbnails = new Dictionary(); - foreach (var factory in _factories) - { - var treeNode = factory.Create(); - var thumbnail = await _displayManager.BuildDisplayAsync(treeNode, _updateModelAccessor.ModelUpdater, "TreeThumbnail"); - thumbnail.Properties["TreeNode"] = treeNode; - thumbnails.Add(factory.Name, thumbnail); - } + AdminMenu = tree, + Thumbnails = thumbnails, + }; - var model = new AdminNodeListViewModel - { - AdminMenu = tree, - Thumbnails = thumbnails, - }; + return model; + } - return model; + public async Task Create(string id, string type) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); } + var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - public async Task Create(string id, string type) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } - var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - - if (adminMenu == null) - { - return NotFound(); - } - - var treeNode = _factories.FirstOrDefault(x => x.Name == type)?.Create(); - - if (treeNode == null) - { - return NotFound(); - } - - var model = new AdminNodeEditViewModel - { - AdminMenuId = id, - AdminNode = treeNode, - AdminNodeId = treeNode.UniqueId, - AdminNodeType = type, - Editor = await _displayManager.BuildEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", "") - }; - - return View(model); + if (adminMenu == null) + { + return NotFound(); } - [HttpPost] - public async Task Create(AdminNodeEditViewModel model) + var treeNode = _factories.FirstOrDefault(x => x.Name == type)?.Create(); + + if (treeNode == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return NotFound(); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.AdminMenuId); + var model = new AdminNodeEditViewModel + { + AdminMenuId = id, + AdminNode = treeNode, + AdminNodeId = treeNode.UniqueId, + AdminNodeType = type, + Editor = await _displayManager.BuildEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", "") + }; + + return View(model); + } - if (adminMenu == null) - { - return NotFound(); - } + [HttpPost] + public async Task Create(AdminNodeEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - var treeNode = _factories.FirstOrDefault(x => x.Name == model.AdminNodeType)?.Create(); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.AdminMenuId); - if (treeNode == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); - editor.Properties["TreeNode"] = treeNode; + var treeNode = _factories.FirstOrDefault(x => x.Name == model.AdminNodeType)?.Create(); - if (ModelState.IsValid) - { - treeNode.UniqueId = model.AdminNodeId; - adminMenu.MenuItems.Add(treeNode); - await _adminMenuService.SaveAsync(adminMenu); + if (treeNode == null) + { + return NotFound(); + } - await _notifier.SuccessAsync(H["Admin node added successfully."]); - return RedirectToAction(nameof(List), new { id = model.AdminMenuId }); - } + var editor = await _displayManager.UpdateEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); + editor.Properties["TreeNode"] = treeNode; - model.Editor = editor; + if (ModelState.IsValid) + { + treeNode.UniqueId = model.AdminNodeId; + adminMenu.MenuItems.Add(treeNode); + await _adminMenuService.SaveAsync(adminMenu); - // If we got this far, something failed, redisplay form - return View(model); + await _notifier.SuccessAsync(H["Admin node added successfully."]); + return RedirectToAction(nameof(List), new { id = model.AdminMenuId }); } - public async Task Edit(string id, string treeNodeId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } - - var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + model.Editor = editor; - if (adminMenu == null) - { - return NotFound(); - } + // If we got this far, something failed, redisplay form + return View(model); + } - var treeNode = adminMenu.GetMenuItemById(treeNodeId); + public async Task Edit(string id, string treeNodeId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - if (treeNode == null) - { - return NotFound(); - } + var adminMenuList = await _adminMenuService.GetAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - var model = new AdminNodeEditViewModel - { - AdminMenuId = id, - AdminNode = treeNode, - AdminNodeId = treeNode.UniqueId, - AdminNodeType = treeNode.GetType().Name, - Priority = treeNode.Priority, - Position = treeNode.Position, - Editor = await _displayManager.BuildEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") - }; + if (adminMenu == null) + { + return NotFound(); + } - model.Editor.TreeNode = treeNode; + var treeNode = adminMenu.GetMenuItemById(treeNodeId); - return View(model); + if (treeNode == null) + { + return NotFound(); } - [HttpPost] - public async Task Edit(AdminNodeEditViewModel model) + var model = new AdminNodeEditViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } - - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.AdminMenuId); + AdminMenuId = id, + AdminNode = treeNode, + AdminNodeId = treeNode.UniqueId, + AdminNodeType = treeNode.GetType().Name, + Priority = treeNode.Priority, + Position = treeNode.Position, + Editor = await _displayManager.BuildEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") + }; + + model.Editor.TreeNode = treeNode; + + return View(model); + } - if (adminMenu == null) - { - return NotFound(); - } + [HttpPost] + public async Task Edit(AdminNodeEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) + { + return Forbid(); + } - var treeNode = adminMenu.GetMenuItemById(model.AdminNodeId); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, model.AdminMenuId); - if (treeNode == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var treeNode = adminMenu.GetMenuItemById(model.AdminNodeId); - if (ModelState.IsValid) - { - treeNode.Priority = model.Priority; - treeNode.Position = model.Position; + if (treeNode == null) + { + return NotFound(); + } - await _adminMenuService.SaveAsync(adminMenu); + var editor = await _displayManager.UpdateEditorAsync(treeNode, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - await _notifier.SuccessAsync(H["Admin node updated successfully."]); - return RedirectToAction(nameof(List), new { id = model.AdminMenuId }); - } + if (ModelState.IsValid) + { + treeNode.Priority = model.Priority; + treeNode.Position = model.Position; - await _notifier.ErrorAsync(H["The admin node has validation errors."]); - model.Editor = editor; + await _adminMenuService.SaveAsync(adminMenu); - // If we got this far, something failed, redisplay form - return View(model); + await _notifier.SuccessAsync(H["Admin node updated successfully."]); + return RedirectToAction(nameof(List), new { id = model.AdminMenuId }); } - [HttpPost] - public async Task Delete(string id, string treeNodeId) + await _notifier.ErrorAsync(H["The admin node has validation errors."]); + model.Editor = editor; + + // If we got this far, something failed, redisplay form + return View(model); + } + + [HttpPost] + public async Task Delete(string id, string treeNodeId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - if (adminMenu == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } - var treeNode = adminMenu.GetMenuItemById(treeNodeId); + var treeNode = adminMenu.GetMenuItemById(treeNodeId); - if (treeNode == null) - { - return NotFound(); - } + if (treeNode == null) + { + return NotFound(); + } - if (adminMenu.RemoveMenuItem(treeNode) == false) - { - return this.InternalServerError(); - } + if (adminMenu.RemoveMenuItem(treeNode) == false) + { + return this.InternalServerError(); + } - await _adminMenuService.SaveAsync(adminMenu); + await _adminMenuService.SaveAsync(adminMenu); - await _notifier.SuccessAsync(H["Admin node deleted successfully."]); + await _notifier.SuccessAsync(H["Admin node deleted successfully."]); - return RedirectToAction(nameof(List), new { id }); - } + return RedirectToAction(nameof(List), new { id }); + } - [HttpPost] - public async Task Toggle(string id, string treeNodeId) + [HttpPost] + public async Task Toggle(string id, string treeNodeId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, id); - if (adminMenu == null) - { - return NotFound(); - } + if (adminMenu == null) + { + return NotFound(); + } - var treeNode = adminMenu.GetMenuItemById(treeNodeId); + var treeNode = adminMenu.GetMenuItemById(treeNodeId); - if (treeNode == null) - { - return NotFound(); - } + if (treeNode == null) + { + return NotFound(); + } - treeNode.Enabled = !treeNode.Enabled; + treeNode.Enabled = !treeNode.Enabled; - await _adminMenuService.SaveAsync(adminMenu); + await _adminMenuService.SaveAsync(adminMenu); - await _notifier.SuccessAsync(H["Admin node toggled successfully."]); + await _notifier.SuccessAsync(H["Admin node toggled successfully."]); - return RedirectToAction(nameof(List), new { id }); - } + return RedirectToAction(nameof(List), new { id }); + } - [HttpPost] - public async Task MoveNode(string treeId, string nodeToMoveId, - string destinationNodeId, int position) + [HttpPost] + public async Task MoveNode(string treeId, string nodeToMoveId, + string destinationNodeId, int position) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAdminMenu)) - { - return Forbid(); - } + return Forbid(); + } - var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); - var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, treeId); + var adminMenuList = await _adminMenuService.LoadAdminMenuListAsync(); + var adminMenu = _adminMenuService.GetAdminMenuById(adminMenuList, treeId); - if ((adminMenu == null) || (adminMenu.MenuItems == null)) - { - return NotFound(); - } + if ((adminMenu == null) || (adminMenu.MenuItems == null)) + { + return NotFound(); + } - var nodeToMove = adminMenu.GetMenuItemById(nodeToMoveId); - if (nodeToMove == null) - { - return NotFound(); - } + var nodeToMove = adminMenu.GetMenuItemById(nodeToMoveId); + if (nodeToMove == null) + { + return NotFound(); + } - var destinationNode = adminMenu.GetMenuItemById(destinationNodeId); // don't check for null. When null the item will be moved to the root. + var destinationNode = adminMenu.GetMenuItemById(destinationNodeId); // don't check for null. When null the item will be moved to the root. - if (adminMenu.RemoveMenuItem(nodeToMove) == false) - { - return StatusCode(500); - } + if (adminMenu.RemoveMenuItem(nodeToMove) == false) + { + return StatusCode(500); + } - if (adminMenu.InsertMenuItemAt(nodeToMove, destinationNode, position) == false) - { - return StatusCode(500); - } + if (adminMenu.InsertMenuItemAt(nodeToMove, destinationNode, position) == false) + { + return StatusCode(500); + } - await _adminMenuService.SaveAsync(adminMenu); + await _adminMenuService.SaveAsync(adminMenu); - return Ok(); - } + return Ok(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentSource.cs index e585f1d7626..9ee17c3b5a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentSource.cs @@ -6,43 +6,42 @@ using OrchardCore.Deployment; using OrchardCore.Json; -namespace OrchardCore.AdminMenu.Deployment +namespace OrchardCore.AdminMenu.Deployment; + +public class AdminMenuDeploymentSource : IDeploymentSource { - public class AdminMenuDeploymentSource : IDeploymentSource + private readonly IAdminMenuService _adminMenuService; + private readonly JsonSerializerOptions _serializationOptions; + + public AdminMenuDeploymentSource(IAdminMenuService adminMenuService, + IOptions serializationOptions) + { + _adminMenuService = adminMenuService; + _serializationOptions = serializationOptions.Value.SerializerOptions; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly IAdminMenuService _adminMenuService; - private readonly JsonSerializerOptions _serializationOptions; + var adminMenuStep = step as AdminMenuDeploymentStep; - public AdminMenuDeploymentSource(IAdminMenuService adminMenuService, - IOptions serializationOptions) + if (adminMenuStep == null) { - _adminMenuService = adminMenuService; - _serializationOptions = serializationOptions.Value.SerializerOptions; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var data = new JsonArray(); + result.Steps.Add(new JsonObject { - var adminMenuStep = step as AdminMenuDeploymentStep; - - if (adminMenuStep == null) - { - return; - } - - var data = new JsonArray(); - result.Steps.Add(new JsonObject - { - ["name"] = "AdminMenu", - ["data"] = data, - }); - - foreach (var adminMenu in (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu) - { - var objectData = JObject.FromObject(adminMenu, _serializationOptions); - data.Add(objectData); - } + ["name"] = "AdminMenu", + ["data"] = data, + }); - return; + foreach (var adminMenu in (await _adminMenuService.GetAdminMenuListAsync()).AdminMenu) + { + var objectData = JObject.FromObject(adminMenu, _serializationOptions); + data.Add(objectData); } + + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStep.cs index 213fec69df6..834535ec055 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.AdminMenu.Deployment +namespace OrchardCore.AdminMenu.Deployment; + +/// +/// Adds all admin menus to a . +/// +public class AdminMenuDeploymentStep : DeploymentStep { - /// - /// Adds all admin menus to a . - /// - public class AdminMenuDeploymentStep : DeploymentStep + public AdminMenuDeploymentStep() { - public AdminMenuDeploymentStep() - { - Name = "AdminMenu"; - } + Name = "AdminMenu"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStepDriver.cs index 1704d77ec50..1d93d456a62 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Deployment/AdminMenuDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AdminMenu.Deployment +namespace OrchardCore.AdminMenu.Deployment; + +public sealed class AdminMenuDeploymentStepDriver : DisplayDriver { - public sealed class AdminMenuDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AdminMenuDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AdminMenuDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AdminMenuDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("AdminMenuDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AdminMenuDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("AdminMenuDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AdminMenuDeploymentStep step, BuildEditorContext context) - { - return View("AdminMenuDeploymentStep_Fields_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AdminMenuDeploymentStep step, BuildEditorContext context) + { + return View("AdminMenuDeploymentStep_Fields_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Models/AdminMenuList.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Models/AdminMenuList.cs index a9f30621d85..b9e58b2fb14 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Models/AdminMenuList.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Models/AdminMenuList.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.AdminMenu.Models +namespace OrchardCore.AdminMenu.Models; + +/// +/// The list of all the AdminMenu stored on the system. +/// +public class AdminMenuList : Document { - /// - /// The list of all the AdminMenu stored on the system. - /// - public class AdminMenuList : Document - { - public List AdminMenu { get; set; } = []; - } + public List AdminMenu { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Recipes/AdminMenuStep.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Recipes/AdminMenuStep.cs index f0fc4e30796..6ea3b102f05 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Recipes/AdminMenuStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Recipes/AdminMenuStep.cs @@ -9,54 +9,53 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.AdminMenu.Recipes +namespace OrchardCore.AdminMenu.Recipes; + +/// +/// This recipe step creates a set of admin menus. +/// +public sealed class AdminMenuStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of admin menus. - /// - public sealed class AdminMenuStep : IRecipeStepHandler + private readonly IAdminMenuService _adminMenuService; + private readonly JsonSerializerOptions _serializationOptions; + + public AdminMenuStep( + IAdminMenuService adminMenuService, + IOptions serializationOptions) { - private readonly IAdminMenuService _adminMenuService; - private readonly JsonSerializerOptions _serializationOptions; + _adminMenuService = adminMenuService; - public AdminMenuStep( - IAdminMenuService adminMenuService, - IOptions serializationOptions) - { - _adminMenuService = adminMenuService; + // The recipe step contains polymorphic types (menu items) which need to be resolved + _serializationOptions = serializationOptions.Value.SerializerOptions; + } - // The recipe step contains polymorphic types (menu items) which need to be resolved - _serializationOptions = serializationOptions.Value.SerializerOptions; + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "AdminMenu", StringComparison.OrdinalIgnoreCase)) + { + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "AdminMenu", StringComparison.OrdinalIgnoreCase)) - { - return; - } + var model = context.Step.ToObject(_serializationOptions); - var model = context.Step.ToObject(_serializationOptions); + foreach (var token in model.Data.Cast()) + { + var adminMenu = token.ToObject(_serializationOptions); - foreach (var token in model.Data.Cast()) + // When the id is not supplied generate an id, otherwise replace the menu if it exists, or create a new menu. + if (string.IsNullOrEmpty(adminMenu.Id)) { - var adminMenu = token.ToObject(_serializationOptions); - - // When the id is not supplied generate an id, otherwise replace the menu if it exists, or create a new menu. - if (string.IsNullOrEmpty(adminMenu.Id)) - { - adminMenu.Id = IdGenerator.GenerateId(); - } - - await _adminMenuService.SaveAsync(adminMenu); + adminMenu.Id = IdGenerator.GenerateId(); } - return; + await _adminMenuService.SaveAsync(adminMenu); } - } - public class AdminMenuStepModel - { - public JsonArray Data { get; set; } + return; } } + +public class AdminMenuStepModel +{ + public JsonArray Data { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuNavigationProvidersCoordinator.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuNavigationProvidersCoordinator.cs index 0568e3d24ae..ed4e778e283 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuNavigationProvidersCoordinator.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuNavigationProvidersCoordinator.cs @@ -6,70 +6,69 @@ using Microsoft.Extensions.Logging; using OrchardCore.Navigation; -namespace OrchardCore.AdminMenu.Services +namespace OrchardCore.AdminMenu.Services; + +// Retrieves all instances of 'IAdminNodeNavigationBuilder'. +// Those are classes that add new 'AdminNodes' to a 'NavigationBuilder' using custom logic specific to the module that register them. +// This class handles their inclusion on the admin menu. +// This class is itself one more 'INavigationProvider' so it can be called from this module's AdminMenu.cs. +public sealed class AdminMenuNavigationProvidersCoordinator : INavigationProvider { - // Retrieves all instances of 'IAdminNodeNavigationBuilder'. - // Those are classes that add new 'AdminNodes' to a 'NavigationBuilder' using custom logic specific to the module that register them. - // This class handles their inclusion on the admin menu. - // This class is itself one more 'INavigationProvider' so it can be called from this module's AdminMenu.cs. - public sealed class AdminMenuNavigationProvidersCoordinator : INavigationProvider + private readonly IAdminMenuService _adminMenuService; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IEnumerable _nodeBuilders; + private readonly ILogger _logger; + + public AdminMenuNavigationProvidersCoordinator( + IAdminMenuService adminMenuService, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor, + IEnumerable nodeBuilders, + ILogger logger) { - private readonly IAdminMenuService _adminMenuService; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IEnumerable _nodeBuilders; - private readonly ILogger _logger; + _adminMenuService = adminMenuService; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + _nodeBuilders = nodeBuilders; + _logger = logger; + } - public AdminMenuNavigationProvidersCoordinator( - IAdminMenuService adminMenuService, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor, - IEnumerable nodeBuilders, - ILogger logger) + public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + // We only add them if the caller uses the string "adminMenu". + if (name != NavigationConstants.AdminMenuId) { - _adminMenuService = adminMenuService; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - _nodeBuilders = nodeBuilders; - _logger = logger; + return; } - public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + var trees = (await _adminMenuService.GetAdminMenuListAsync()) + .AdminMenu.Where(m => m.Enabled && m.MenuItems.Count > 0); + + foreach (var tree in trees) { - // We only add them if the caller uses the string "adminMenu". - if (name != NavigationConstants.AdminMenuId) + if (await _authorizationService.AuthorizeAsync( + _httpContextAccessor.HttpContext?.User, + Permissions.CreatePermissionForAdminMenu(tree.Name))) { - return; + await BuildTreeAsync(tree, builder); } + } + } - var trees = (await _adminMenuService.GetAdminMenuListAsync()) - .AdminMenu.Where(m => m.Enabled && m.MenuItems.Count > 0); + private async Task BuildTreeAsync(Models.AdminMenu tree, NavigationBuilder builder) + { + foreach (var node in tree.MenuItems) + { + var nodeBuilder = _nodeBuilders.FirstOrDefault(x => x.Name == node.GetType().Name); - foreach (var tree in trees) + if (nodeBuilder != null) { - if (await _authorizationService.AuthorizeAsync( - _httpContextAccessor.HttpContext?.User, - Permissions.CreatePermissionForAdminMenu(tree.Name))) - { - await BuildTreeAsync(tree, builder); - } + await nodeBuilder.BuildNavigationAsync(node, builder, _nodeBuilders); } - } - - private async Task BuildTreeAsync(Models.AdminMenu tree, NavigationBuilder builder) - { - foreach (var node in tree.MenuItems) + else { - var nodeBuilder = _nodeBuilders.FirstOrDefault(x => x.Name == node.GetType().Name); - - if (nodeBuilder != null) - { - await nodeBuilder.BuildNavigationAsync(node, builder, _nodeBuilders); - } - else - { - _logger.LogError("No Builder registered for admin node of type '{TreeNodeName}'", node.GetType().Name); - } + _logger.LogError("No Builder registered for admin node of type '{TreeNodeName}'", node.GetType().Name); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuService.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuService.cs index 6eec990cb21..97e14ef9ded 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuService.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/AdminMenuService.cs @@ -4,58 +4,57 @@ using OrchardCore.AdminMenu.Models; using OrchardCore.Documents; -namespace OrchardCore.AdminMenu.Services +namespace OrchardCore.AdminMenu.Services; + +public class AdminMenuService : IAdminMenuService { - public class AdminMenuService : IAdminMenuService - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public AdminMenuService(IDocumentManager documentManager) => _documentManager = documentManager; + public AdminMenuService(IDocumentManager documentManager) => _documentManager = documentManager; - /// - /// Loads the admin menus from the store for updating and that should not be cached. - /// - public Task LoadAdminMenuListAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Loads the admin menus from the store for updating and that should not be cached. + /// + public Task LoadAdminMenuListAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - /// Gets the admin menus from the cache for sharing and that should not be updated. - /// - public Task GetAdminMenuListAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + /// Gets the admin menus from the cache for sharing and that should not be updated. + /// + public Task GetAdminMenuListAsync() => _documentManager.GetOrCreateImmutableAsync(); + + public async Task SaveAsync(Models.AdminMenu tree) + { + var adminMenuList = await LoadAdminMenuListAsync(); - public async Task SaveAsync(Models.AdminMenu tree) + var preexisting = adminMenuList.AdminMenu.FirstOrDefault(x => string.Equals(x.Id, tree.Id, StringComparison.OrdinalIgnoreCase)); + + // it's new? add it + if (preexisting == null) { - var adminMenuList = await LoadAdminMenuListAsync(); - - var preexisting = adminMenuList.AdminMenu.FirstOrDefault(x => string.Equals(x.Id, tree.Id, StringComparison.OrdinalIgnoreCase)); - - // it's new? add it - if (preexisting == null) - { - adminMenuList.AdminMenu.Add(tree); - } - else // not new: replace it - { - var index = adminMenuList.AdminMenu.IndexOf(preexisting); - adminMenuList.AdminMenu[index] = tree; - } - - await _documentManager.UpdateAsync(adminMenuList); + adminMenuList.AdminMenu.Add(tree); } - - public Models.AdminMenu GetAdminMenuById(AdminMenuList adminMenuList, string id) + else // not new: replace it { - return adminMenuList.AdminMenu.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + var index = adminMenuList.AdminMenu.IndexOf(preexisting); + adminMenuList.AdminMenu[index] = tree; } - public async Task DeleteAsync(Models.AdminMenu tree) - { - var adminMenuList = await LoadAdminMenuListAsync(); + await _documentManager.UpdateAsync(adminMenuList); + } - var count = adminMenuList.AdminMenu.RemoveAll(x => string.Equals(x.Id, tree.Id, StringComparison.OrdinalIgnoreCase)); + public Models.AdminMenu GetAdminMenuById(AdminMenuList adminMenuList, string id) + { + return adminMenuList.AdminMenu.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + } - await _documentManager.UpdateAsync(adminMenuList); + public async Task DeleteAsync(Models.AdminMenu tree) + { + var adminMenuList = await LoadAdminMenuListAsync(); - return count; - } + var count = adminMenuList.AdminMenu.RemoveAll(x => string.Equals(x.Id, tree.Id, StringComparison.OrdinalIgnoreCase)); + + await _documentManager.UpdateAsync(adminMenuList); + + return count; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/IAdminMenuService.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/IAdminMenuService.cs index 614ad53e5e9..37a6d2b0046 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/IAdminMenuService.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Services/IAdminMenuService.cs @@ -1,39 +1,38 @@ using System.Threading.Tasks; -namespace OrchardCore.AdminMenu.Services +namespace OrchardCore.AdminMenu.Services; + +/// +/// Provides services to manage the admin menus. +/// +public interface IAdminMenuService { /// - /// Provides services to manage the admin menus. + /// Loads the admin menus from the store for updating and that should not be cached. /// - public interface IAdminMenuService - { - /// - /// Loads the admin menus from the store for updating and that should not be cached. - /// - Task LoadAdminMenuListAsync(); + Task LoadAdminMenuListAsync(); - /// - /// Gets the admin menus from the cache for sharing and that should not be updated. - /// - Task GetAdminMenuListAsync(); + /// + /// Gets the admin menus from the cache for sharing and that should not be updated. + /// + Task GetAdminMenuListAsync(); - /// - /// Persist an admin menu. - /// - /// - /// - Task SaveAsync(Models.AdminMenu tree); + /// + /// Persist an admin menu. + /// + /// + /// + Task SaveAsync(Models.AdminMenu tree); - /// - /// Returns an admin menu. - /// - Models.AdminMenu GetAdminMenuById(Models.AdminMenuList adminMenuList, string id); + /// + /// Returns an admin menu. + /// + Models.AdminMenu GetAdminMenuById(Models.AdminMenuList adminMenuList, string id); - /// - /// Deletes an admin menu. - /// - /// - /// The count of deleted items. - Task DeleteAsync(Models.AdminMenu tree); - } + /// + /// Deletes an admin menu. + /// + /// + /// The count of deleted items. + Task DeleteAsync(Models.AdminMenu tree); } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Startup.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Startup.cs index 2b2099f5f02..9a639e053c4 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Startup.cs @@ -9,28 +9,27 @@ using OrchardCore.Recipes; using OrchardCore.Security.Permissions; -namespace OrchardCore.AdminMenu +namespace OrchardCore.AdminMenu; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.AddDeployment(); + services.AddDeployment(); - // placeholder treeNode - services.AddAdminNode(); + // placeholder treeNode + services.AddAdminNode(); - // link treeNode - services.AddAdminNode(); - } + // link treeNode + services.AddAdminNode(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuCreateViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuCreateViewModel.cs index 54292c428e4..ce360f15f4c 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuCreateViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuCreateViewModel.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.AdminMenu.ViewModels +namespace OrchardCore.AdminMenu.ViewModels; + +public class AdminMenuCreateViewModel { - public class AdminMenuCreateViewModel - { - [Required] - public string Name { get; set; } - } + [Required] + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuEditViewModel.cs index 0478a97a188..823f9cebc67 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuEditViewModel.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.AdminMenu.ViewModels +namespace OrchardCore.AdminMenu.ViewModels; + +public class AdminMenuEditViewModel { - public class AdminMenuEditViewModel - { - public string Id { get; set; } + public string Id { get; set; } - [Required] - public string Name { get; set; } - } + [Required] + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuListViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuListViewModel.cs index 965d97f926c..9ea3bfb0c81 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuListViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminMenuListViewModel.cs @@ -2,37 +2,36 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.AdminMenu.ViewModels +namespace OrchardCore.AdminMenu.ViewModels; + +public class AdminMenuListViewModel +{ + public IList AdminMenu { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + public dynamic Pager { get; set; } +} + +public class AdminMenuEntry +{ + public Models.AdminMenu AdminMenu { get; set; } + public bool IsChecked { get; set; } +} + +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + + #endregion Lists to populate +} + +public enum ContentsBulkAction { - public class AdminMenuListViewModel - { - public IList AdminMenu { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - public dynamic Pager { get; set; } - } - - public class AdminMenuEntry - { - public Models.AdminMenu AdminMenu { get; set; } - public bool IsChecked { get; set; } - } - - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } - - #region Lists to populate - - [BindNever] - public List ContentsBulkAction { get; set; } - - #endregion Lists to populate - } - - public enum ContentsBulkAction - { - None, - Remove - } + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeEditViewModel.cs index 294f97ba1bd..8ee2c8c1cff 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeEditViewModel.cs @@ -1,20 +1,19 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.AdminMenu.Models; -namespace OrchardCore.AdminMenu.ViewModels +namespace OrchardCore.AdminMenu.ViewModels; + +public class AdminNodeEditViewModel { - public class AdminNodeEditViewModel - { - public string AdminMenuId { get; set; } - public string AdminNodeId { get; set; } - public string AdminNodeType { get; set; } + public string AdminMenuId { get; set; } + public string AdminNodeId { get; set; } + public string AdminNodeType { get; set; } - public int Priority { get; set; } - public string Position { get; set; } + public int Priority { get; set; } + public string Position { get; set; } - public dynamic Editor { get; set; } + public dynamic Editor { get; set; } - [BindNever] - public AdminNode AdminNode { get; set; } - } + [BindNever] + public AdminNode AdminNode { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeListViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeListViewModel.cs index d2f084a5b21..2200f1303cb 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeListViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/ViewModels/AdminNodeListViewModel.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace OrchardCore.AdminMenu.ViewModels +namespace OrchardCore.AdminMenu.ViewModels; + +public class AdminNodeListViewModel { - public class AdminNodeListViewModel - { - public Models.AdminMenu AdminMenu { get; set; } - public IDictionary Thumbnails { get; set; } - } + public Models.AdminMenu AdminMenu { get; set; } + public IDictionary Thumbnails { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Drivers/AliasPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Drivers/AliasPartDisplayDriver.cs index dbaa430ff9c..b50cce1f4dd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Drivers/AliasPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Drivers/AliasPartDisplayDriver.cs @@ -9,46 +9,45 @@ using OrchardCore.Mvc.ModelBinding; using YesSql; -namespace OrchardCore.Alias.Drivers +namespace OrchardCore.Alias.Drivers; + +public sealed class AliasPartDisplayDriver : ContentPartDisplayDriver { - public sealed class AliasPartDisplayDriver : ContentPartDisplayDriver + private readonly ISession _session; + + internal readonly IStringLocalizer S; + + public AliasPartDisplayDriver( + ISession session, + IStringLocalizer localizer + ) { - private readonly ISession _session; + _session = session; + S = localizer; + } - internal readonly IStringLocalizer S; + public override IDisplayResult Edit(AliasPart aliasPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), m => BuildViewModel(m, aliasPart, context.TypePartDefinition.GetSettings())); + } - public AliasPartDisplayDriver( - ISession session, - IStringLocalizer localizer - ) - { - _session = session; - S = localizer; - } + public override async Task UpdateAsync(AliasPart model, UpdatePartEditorContext context) + { + await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Alias); - public override IDisplayResult Edit(AliasPart aliasPart, BuildPartEditorContext context) + await foreach (var item in model.ValidateAsync(S, _session)) { - return Initialize(GetEditorShapeType(context), m => BuildViewModel(m, aliasPart, context.TypePartDefinition.GetSettings())); + context.Updater.ModelState.BindValidationResult(Prefix, item); } - public override async Task UpdateAsync(AliasPart model, UpdatePartEditorContext context) - { - await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Alias); - - await foreach (var item in model.ValidateAsync(S, _session)) - { - context.Updater.ModelState.BindValidationResult(Prefix, item); - } - - return Edit(model, context); - } + return Edit(model, context); + } - private static void BuildViewModel(AliasPartViewModel model, AliasPart part, AliasPartSettings settings) - { - model.Alias = part.Alias; - model.AliasPart = part; - model.ContentItem = part.ContentItem; - model.Settings = settings; - } + private static void BuildViewModel(AliasPartViewModel model, AliasPart part, AliasPartSettings settings) + { + model.Alias = part.Alias; + model.AliasPart = part; + model.ContentItem = part.ContentItem; + model.Settings = settings; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs index 29781175cf6..e6f61ee738e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasInputObjectType.cs @@ -3,16 +3,15 @@ using OrchardCore.Alias.Models; using OrchardCore.Apis.GraphQL.Queries; -namespace OrchardCore.Alias.GraphQL +namespace OrchardCore.Alias.GraphQL; + +public class AliasInputObjectType : WhereInputObjectGraphType { - public class AliasInputObjectType : WhereInputObjectGraphType + public AliasInputObjectType(IStringLocalizer S) { - public AliasInputObjectType(IStringLocalizer S) - { - Name = "AliasPartInput"; - Description = S["the alias part of the content item"]; + Name = "AliasPartInput"; + Description = S["the alias part of the content item"]; - AddScalarFilterFields("alias", S["the alias of the content item"]); - } + AddScalarFilterFields("alias", S["the alias of the content item"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasPartIndexAliasProvider.cs b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasPartIndexAliasProvider.cs index 3e6b18dc6ae..75612939d6b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasPartIndexAliasProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasPartIndexAliasProvider.cs @@ -3,23 +3,22 @@ using OrchardCore.Alias.Indexes; using OrchardCore.ContentManagement.GraphQL.Queries; -namespace OrchardCore.Alias.GraphQL -{ - public class AliasPartIndexAliasProvider : IIndexAliasProvider - { - private static readonly IndexAlias[] _aliases = - [ - new IndexAlias - { - Alias = "aliasPart", - Index = "AliasPartIndex", - IndexType = typeof(AliasPartIndex) - } - ]; +namespace OrchardCore.Alias.GraphQL; - public ValueTask> GetAliasesAsync() +public class AliasPartIndexAliasProvider : IIndexAliasProvider +{ + private static readonly IndexAlias[] _aliases = + [ + new IndexAlias { - return ValueTask.FromResult>(_aliases); + Alias = "aliasPart", + Index = "AliasPartIndex", + IndexType = typeof(AliasPartIndex) } + ]; + + public ValueTask> GetAliasesAsync() + { + return ValueTask.FromResult>(_aliases); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasQueryObjectType.cs index 4b4c635966e..0ef09ae31f2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/AliasQueryObjectType.cs @@ -2,16 +2,15 @@ using Microsoft.Extensions.Localization; using OrchardCore.Alias.Models; -namespace OrchardCore.Alias.GraphQL +namespace OrchardCore.Alias.GraphQL; + +public class AliasQueryObjectType : ObjectGraphType { - public class AliasQueryObjectType : ObjectGraphType + public AliasQueryObjectType(IStringLocalizer S) { - public AliasQueryObjectType(IStringLocalizer S) - { - Name = "AliasPart"; - Description = S["Alternative path for the content item"]; + Name = "AliasPart"; + Description = S["Alternative path for the content item"]; - Field("alias", x => x.Alias, true); - } + Field("alias", x => x.Alias, true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/Startup.cs index a52c2196878..ac5587c2b82 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/GraphQL/Startup.cs @@ -6,17 +6,16 @@ using OrchardCore.ContentManagement.GraphQL.Queries; using OrchardCore.Modules; -namespace OrchardCore.Alias.GraphQL +namespace OrchardCore.Alias.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddInputObjectGraphType(); - services.AddTransient(); - services.AddWhereInputIndexPropertyProvider(); - } + services.AddObjectGraphType(); + services.AddInputObjectGraphType(); + services.AddTransient(); + services.AddWhereInputIndexPropertyProvider(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Handlers/AliasPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Handlers/AliasPartHandler.cs index e3cf7c4210c..437e4dd3395 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Handlers/AliasPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Handlers/AliasPartHandler.cs @@ -15,147 +15,146 @@ using OrchardCore.Liquid; using YesSql; -namespace OrchardCore.Alias.Handlers +namespace OrchardCore.Alias.Handlers; + +public class AliasPartHandler : ContentPartHandler { - public class AliasPartHandler : ContentPartHandler + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ITagCache _tagCache; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly ISession _session; + protected readonly IStringLocalizer S; + + public AliasPartHandler( + IContentDefinitionManager contentDefinitionManager, + ITagCache tagCache, + ILiquidTemplateManager liquidTemplateManager, + ISession session, + IStringLocalizer stringLocalizer) + { + _contentDefinitionManager = contentDefinitionManager; + _tagCache = tagCache; + _liquidTemplateManager = liquidTemplateManager; + _session = session; + S = stringLocalizer; + } + + public override async Task ValidatingAsync(ValidateContentContext context, AliasPart part) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ITagCache _tagCache; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly ISession _session; - protected readonly IStringLocalizer S; - - public AliasPartHandler( - IContentDefinitionManager contentDefinitionManager, - ITagCache tagCache, - ILiquidTemplateManager liquidTemplateManager, - ISession session, - IStringLocalizer stringLocalizer) + // Only validate the alias if it's not empty. + if (string.IsNullOrWhiteSpace(part.Alias)) { - _contentDefinitionManager = contentDefinitionManager; - _tagCache = tagCache; - _liquidTemplateManager = liquidTemplateManager; - _session = session; - S = stringLocalizer; + return; } - public override async Task ValidatingAsync(ValidateContentContext context, AliasPart part) + await foreach (var item in part.ValidateAsync(S, _session)) { - // Only validate the alias if it's not empty. - if (string.IsNullOrWhiteSpace(part.Alias)) - { - return; - } - - await foreach (var item in part.ValidateAsync(S, _session)) - { - context.Fail(item); - } + context.Fail(item); } + } - public async override Task UpdatedAsync(UpdateContentContext context, AliasPart part) + public async override Task UpdatedAsync(UpdateContentContext context, AliasPart part) + { + // Compute the Alias only if it's empty. + if (!string.IsNullOrEmpty(part.Alias)) { - // Compute the Alias only if it's empty. - if (!string.IsNullOrEmpty(part.Alias)) - { - return; - } + return; + } - var pattern = await GetPatternAsync(part); + var pattern = await GetPatternAsync(part); - if (!string.IsNullOrEmpty(pattern)) + if (!string.IsNullOrEmpty(pattern)) + { + var model = new AliasPartViewModel() { - var model = new AliasPartViewModel() - { - Alias = part.Alias, - AliasPart = part, - ContentItem = part.ContentItem - }; + Alias = part.Alias, + AliasPart = part, + ContentItem = part.ContentItem + }; - part.Alias = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, - new Dictionary() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) }); + part.Alias = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, + new Dictionary() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) }); - part.Alias = part.Alias.Replace("\r", string.Empty).Replace("\n", string.Empty); + part.Alias = part.Alias.Replace("\r", string.Empty).Replace("\n", string.Empty); - if (part.Alias?.Length > AliasPart.MaxAliasLength) - { - part.Alias = part.Alias[..AliasPart.MaxAliasLength]; - } - - if (!await part.IsAliasUniqueAsync(_session, part.Alias)) - { - part.Alias = await GenerateUniqueAliasAsync(part.Alias, part); - } - - part.Apply(); + if (part.Alias?.Length > AliasPart.MaxAliasLength) + { + part.Alias = part.Alias[..AliasPart.MaxAliasLength]; } - } - - public override Task PublishedAsync(PublishContentContext context, AliasPart instance) - { - return _tagCache.RemoveTagAsync($"alias:{instance.Alias}"); - } - public override Task RemovedAsync(RemoveContentContext context, AliasPart instance) - { - if (context.NoActiveVersionLeft) + if (!await part.IsAliasUniqueAsync(_session, part.Alias)) { - return _tagCache.RemoveTagAsync($"alias:{instance.Alias}"); + part.Alias = await GenerateUniqueAliasAsync(part.Alias, part); } - return Task.CompletedTask; + part.Apply(); } + } + + public override Task PublishedAsync(PublishContentContext context, AliasPart instance) + { + return _tagCache.RemoveTagAsync($"alias:{instance.Alias}"); + } - public override Task UnpublishedAsync(PublishContentContext context, AliasPart instance) + public override Task RemovedAsync(RemoveContentContext context, AliasPart instance) + { + if (context.NoActiveVersionLeft) { return _tagCache.RemoveTagAsync($"alias:{instance.Alias}"); } - public override async Task CloningAsync(CloneContentContext context, AliasPart part) - { - var clonedPart = context.CloneContentItem.As(); - clonedPart.Alias = await GenerateUniqueAliasAsync(part.Alias, clonedPart); + return Task.CompletedTask; + } - clonedPart.Apply(); - } + public override Task UnpublishedAsync(PublishContentContext context, AliasPart instance) + { + return _tagCache.RemoveTagAsync($"alias:{instance.Alias}"); + } - /// - /// Get the pattern from the AliasPartSettings property for its type. - /// - private async Task GetPatternAsync(AliasPart part) - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(AliasPart), StringComparison.Ordinal)); - var pattern = contentTypePartDefinition.GetSettings().Pattern; + public override async Task CloningAsync(CloneContentContext context, AliasPart part) + { + var clonedPart = context.CloneContentItem.As(); + clonedPart.Alias = await GenerateUniqueAliasAsync(part.Alias, clonedPart); - return pattern; - } + clonedPart.Apply(); + } - private async Task GenerateUniqueAliasAsync(string alias, AliasPart context) + /// + /// Get the pattern from the AliasPartSettings property for its type. + /// + private async Task GetPatternAsync(AliasPart part) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(AliasPart), StringComparison.Ordinal)); + var pattern = contentTypePartDefinition.GetSettings().Pattern; + + return pattern; + } + + private async Task GenerateUniqueAliasAsync(string alias, AliasPart context) + { + var version = 1; + var unversionedAlias = alias; + + var versionSeparatorPosition = alias.LastIndexOf('-'); + if (versionSeparatorPosition > -1 && int.TryParse(alias[versionSeparatorPosition..].TrimStart('-'), out version)) { - var version = 1; - var unversionedAlias = alias; + unversionedAlias = alias[..versionSeparatorPosition]; + } - var versionSeparatorPosition = alias.LastIndexOf('-'); - if (versionSeparatorPosition > -1 && int.TryParse(alias[versionSeparatorPosition..].TrimStart('-'), out version)) + while (true) + { + // Unversioned length + separator char + version length. + var quantityCharactersToTrim = unversionedAlias.Length + 1 + version.ToString().Length - AliasPart.MaxAliasLength; + if (quantityCharactersToTrim > 0) { - unversionedAlias = alias[..versionSeparatorPosition]; + unversionedAlias = unversionedAlias[..^quantityCharactersToTrim]; } - while (true) + var versionedAlias = $"{unversionedAlias}-{version++}"; + if (await context.IsAliasUniqueAsync(_session, versionedAlias)) { - // Unversioned length + separator char + version length. - var quantityCharactersToTrim = unversionedAlias.Length + 1 + version.ToString().Length - AliasPart.MaxAliasLength; - if (quantityCharactersToTrim > 0) - { - unversionedAlias = unversionedAlias[..^quantityCharactersToTrim]; - } - - var versionedAlias = $"{unversionedAlias}-{version++}"; - if (await context.IsAliasUniqueAsync(_session, versionedAlias)) - { - return versionedAlias; - } + return versionedAlias; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Indexes/AliasPartIndex.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Indexes/AliasPartIndex.cs index 632d326e7b2..026182a6ff8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Indexes/AliasPartIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Indexes/AliasPartIndex.cs @@ -10,80 +10,79 @@ using OrchardCore.Data; using YesSql.Indexes; -namespace OrchardCore.Alias.Indexes +namespace OrchardCore.Alias.Indexes; + +public class AliasPartIndex : MapIndex { - public class AliasPartIndex : MapIndex + public string ContentItemId { get; set; } + public string Alias { get; set; } + public bool Latest { get; set; } + public bool Published { get; set; } +} + +public class AliasPartIndexProvider : ContentHandlerBase, IIndexProvider, IScopedIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _partRemoved = []; + private IContentDefinitionManager _contentDefinitionManager; + + public AliasPartIndexProvider(IServiceProvider serviceProvider) { - public string ContentItemId { get; set; } - public string Alias { get; set; } - public bool Latest { get; set; } - public bool Published { get; set; } + _serviceProvider = serviceProvider; } - public class AliasPartIndexProvider : ContentHandlerBase, IIndexProvider, IScopedIndexProvider + public override async Task UpdatedAsync(UpdateContentContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _partRemoved = []; - private IContentDefinitionManager _contentDefinitionManager; + var part = context.ContentItem.As(); - public AliasPartIndexProvider(IServiceProvider serviceProvider) + // Validate that the content definition contains this part, this prevents indexing parts + // that have been removed from the type definition, but are still present in the elements. + if (part != null) { - _serviceProvider = serviceProvider; - } - - public override async Task UpdatedAsync(UpdateContentContext context) - { - var part = context.ContentItem.As(); + // Lazy initialization because of ISession cyclic dependency. + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Validate that the content definition contains this part, this prevents indexing parts - // that have been removed from the type definition, but are still present in the elements. - if (part != null) + // Search for this part. + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); + if (contentTypeDefinition?.Parts is not null && !contentTypeDefinition.Parts.Any(ctd => string.Equals(ctd.Name, nameof(AliasPart), StringComparison.Ordinal))) { - // Lazy initialization because of ISession cyclic dependency. - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - - // Search for this part. - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - if (contentTypeDefinition?.Parts is not null && !contentTypeDefinition.Parts.Any(ctd => string.Equals(ctd.Name, nameof(AliasPart), StringComparison.Ordinal))) - { - context.ContentItem.Remove(); - _partRemoved.Add(context.ContentItem.ContentItemId); - } + context.ContentItem.Remove(); + _partRemoved.Add(context.ContentItem.ContentItemId); } } + } - public string CollectionName { get; set; } + public string CollectionName { get; set; } - public Type ForType() => typeof(ContentItem); + public Type ForType() => typeof(ContentItem); - public void Describe(IDescriptor context) => Describe((DescribeContext)context); + public void Describe(IDescriptor context) => Describe((DescribeContext)context); - public void Describe(DescribeContext context) - { - context.For() - .When(contentItem => contentItem.Has() || _partRemoved.Contains(contentItem.ContentItemId)) - .Map(contentItem => + public void Describe(DescribeContext context) + { + context.For() + .When(contentItem => contentItem.Has() || _partRemoved.Contains(contentItem.ContentItemId)) + .Map(contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - var part = contentItem.As(); - if (part == null || string.IsNullOrEmpty(part.Alias)) - { - return null; - } + var part = contentItem.As(); + if (part == null || string.IsNullOrEmpty(part.Alias)) + { + return null; + } - return new AliasPartIndex - { - Alias = part.Alias.ToLowerInvariant(), - ContentItemId = contentItem.ContentItemId, - Latest = contentItem.Latest, - Published = contentItem.Published - }; - }); - } + return new AliasPartIndex + { + Alias = part.Alias.ToLowerInvariant(), + ContentItemId = contentItem.ContentItemId, + Latest = contentItem.Latest, + Published = contentItem.Published + }; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Indexing/AliasPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Indexing/AliasPartIndexHandler.cs index 3268267d6bf..12d4549754a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Indexing/AliasPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Indexing/AliasPartIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.Alias.Models; using OrchardCore.Indexing; -namespace OrchardCore.Alias.Indexing +namespace OrchardCore.Alias.Indexing; + +public class AliasPartIndexHandler : ContentPartIndexHandler { - public class AliasPartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(AliasPart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(AliasPart part, BuildPartIndexContext context) - { - var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, part.Alias, options); - } + var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, part.Alias, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Migrations.cs index c837411c4a6..88fe28fcd6d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Migrations.cs @@ -6,91 +6,90 @@ using OrchardCore.Data.Migration; using YesSql.Sql; -namespace OrchardCore.Alias +namespace OrchardCore.Alias; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(AliasPart), builder => builder - .Attachable() - .WithDescription("Provides a way to define custom aliases for content items.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(AliasPart), builder => builder + .Attachable() + .WithDescription("Provides a way to define custom aliases for content items.")); - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("Alias", col => col.WithLength(AliasPart.MaxAliasLength)) - .Column("ContentItemId", c => c.WithLength(26)) - .Column("Latest", c => c.WithDefault(false)) - .Column("Published", c => c.WithDefault(true)) - ); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("Alias", col => col.WithLength(AliasPart.MaxAliasLength)) + .Column("ContentItemId", c => c.WithLength(26)) + .Column("Latest", c => c.WithDefault(false)) + .Column("Published", c => c.WithDefault(true)) + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_AliasPartIndex_DocumentId", - "DocumentId", - "Alias", - "ContentItemId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_AliasPartIndex_DocumentId", + "DocumentId", + "Alias", + "ContentItemId", + "Published", + "Latest") + ); - // Shortcut other migration steps on new content definition schemas. - return 4; - } + // Shortcut other migration steps on new content definition schemas. + return 4; + } - // This code can be removed in a later version as Latest and Published are alterations. - public async Task UpdateFrom1Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Latest", c => c.WithDefault(false)) - ); + // This code can be removed in a later version as Latest and Published are alterations. + public async Task UpdateFrom1Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Latest", c => c.WithDefault(false)) + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Published", c => c.WithDefault(true)) - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Published", c => c.WithDefault(true)) + ); - return 2; - } + return 2; + } - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - // Can't be fully created on existing databases where the 'Alias' may be of 767 chars. - await SchemaBuilder.AlterIndexTableAsync(table => table - // .CreateIndex("IDX_AliasPartIndex_DocumentId", - // "DocumentId", - // "Alias", - // "ContentItemId", - // "Latest", - // "Published") + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + // Can't be fully created on existing databases where the 'Alias' may be of 767 chars. + await SchemaBuilder.AlterIndexTableAsync(table => table + // .CreateIndex("IDX_AliasPartIndex_DocumentId", + // "DocumentId", + // "Alias", + // "ContentItemId", + // "Latest", + // "Published") - .CreateIndex("IDX_AliasPartIndex_DocumentId", - "DocumentId", - "Alias") - ); + .CreateIndex("IDX_AliasPartIndex_DocumentId", + "DocumentId", + "Alias") + ); - return 3; - } + return 3; + } - // This code can be removed in a later version. - public async Task UpdateFrom3Async() - { - // In the previous migration step, an index was not fully created, - // but here, we can create a separate one for the missing columns. - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_AliasPartIndex_DocumentId_ContentItemId", - "DocumentId", - "ContentItemId", - "Published", - "Latest") - ); + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + // In the previous migration step, an index was not fully created, + // but here, we can create a separate one for the missing columns. + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_AliasPartIndex_DocumentId_ContentItemId", + "DocumentId", + "ContentItemId", + "Published", + "Latest") + ); - return 4; - } + return 4; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPart.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPart.cs index 52c2517702d..2dc87f91382 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPart.cs @@ -1,15 +1,14 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Alias.Models +namespace OrchardCore.Alias.Models; + +public class AliasPart : ContentPart { - public class AliasPart : ContentPart - { - // Maximum length that MySql can support in an index under utf8mb4 collation is 768, - // minus 2 for the `DocumentId` integer (bigint size = 8 bytes = 2 character size), - // minus 26 for the `ContentItemId` and 1 for the 'Published' and 'Latest' bools. - // minus 4 to allow at least to add a new integer column. - public const int MaxAliasLength = 735; + // Maximum length that MySql can support in an index under utf8mb4 collation is 768, + // minus 2 for the `DocumentId` integer (bigint size = 8 bytes = 2 character size), + // minus 26 for the `ContentItemId` and 1 for the 'Published' and 'Latest' bools. + // minus 4 to allow at least to add a new integer column. + public const int MaxAliasLength = 735; - public string Alias { get; set; } - } + public string Alias { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPartExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPartExtensions.cs index 2d780605935..027fef2a245 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPartExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Models/AliasPartExtensions.cs @@ -5,30 +5,29 @@ using OrchardCore.Alias.Indexes; using YesSql; -namespace OrchardCore.Alias.Models +namespace OrchardCore.Alias.Models; + +public static class AliasPartExtensions { - public static class AliasPartExtensions + public static async IAsyncEnumerable ValidateAsync(this AliasPart part, IStringLocalizer S, ISession session) { - public static async IAsyncEnumerable ValidateAsync(this AliasPart part, IStringLocalizer S, ISession session) + if (!string.IsNullOrWhiteSpace(part.Alias)) { - if (!string.IsNullOrWhiteSpace(part.Alias)) + if (part.Alias.Length > AliasPart.MaxAliasLength) { - if (part.Alias.Length > AliasPart.MaxAliasLength) - { - yield return new ValidationResult(S["Your alias is too long. The alias can only be up to {0} characters. \"{1}\"", AliasPart.MaxAliasLength, part.Alias], new string[] { nameof(part.Alias) }); - } - - if (!await IsAliasUniqueAsync(part, session, part.Alias)) - { - yield return new ValidationResult(S["Your alias is already in use. \"{0}\"", part.Alias], new[] { nameof(part.Alias) }); - } + yield return new ValidationResult(S["Your alias is too long. The alias can only be up to {0} characters. \"{1}\"", AliasPart.MaxAliasLength, part.Alias], new string[] { nameof(part.Alias) }); } - } - public static async Task IsAliasUniqueAsync(this AliasPart context, ISession session, string alias) - { - return (await session.QueryIndex(o => o.Alias == alias && o.ContentItemId != context.ContentItem.ContentItemId).CountAsync()) == 0; + if (!await IsAliasUniqueAsync(part, session, part.Alias)) + { + yield return new ValidationResult(S["Your alias is already in use. \"{0}\"", part.Alias], new[] { nameof(part.Alias) }); + } } + } + public static async Task IsAliasUniqueAsync(this AliasPart context, ISession session, string alias) + { + return (await session.QueryIndex(o => o.Alias == alias && o.ContentItemId != context.ContentItem.ContentItemId).CountAsync()) == 0; } + } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Services/AliasPartContentHandleProvider.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Services/AliasPartContentHandleProvider.cs index 8388a62709d..bf2dcda5bb9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Services/AliasPartContentHandleProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Services/AliasPartContentHandleProvider.cs @@ -4,38 +4,37 @@ using OrchardCore.ContentManagement; using YesSql; -namespace OrchardCore.Alias.Services +namespace OrchardCore.Alias.Services; + +public class AliasPartContentHandleProvider : IContentHandleProvider { - public class AliasPartContentHandleProvider : IContentHandleProvider - { - private readonly ISession _session; + private readonly ISession _session; - public AliasPartContentHandleProvider(ISession session) - { - _session = session; - } + public AliasPartContentHandleProvider(ISession session) + { + _session = session; + } - public int Order => 100; + public int Order => 100; - public async Task GetContentItemIdAsync(string handle) + public async Task GetContentItemIdAsync(string handle) + { + if (handle.StartsWith("alias:", StringComparison.OrdinalIgnoreCase)) { - if (handle.StartsWith("alias:", StringComparison.OrdinalIgnoreCase)) - { - handle = handle[6..]; + handle = handle[6..]; - var aliasPartIndex = await AliasPartContentHandleHelper.QueryAliasIndex(_session, handle); - return aliasPartIndex?.ContentItemId; - } - - return null; + var aliasPartIndex = await AliasPartContentHandleHelper.QueryAliasIndex(_session, handle); + return aliasPartIndex?.ContentItemId; } + + return null; } +} - internal sealed class AliasPartContentHandleHelper - { +internal sealed class AliasPartContentHandleHelper +{ #pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons - internal static Task QueryAliasIndex(ISession session, string alias) => - session.Query(x => x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync(); + internal static Task QueryAliasIndex(ISession session, string alias) => + session.Query(x => x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync(); #pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettings.cs index 015b3012083..c0a800266a9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettings.cs @@ -1,22 +1,21 @@ using System.ComponentModel; -namespace OrchardCore.Alias.Settings +namespace OrchardCore.Alias.Settings; + +public enum AliasPartOptions { - public enum AliasPartOptions - { - Editable, - GeneratedDisabled - } + Editable, + GeneratedDisabled +} - public class AliasPartSettings - { - [DefaultValue("{{ Model.ContentItem.DisplayText | slugify }}")] - public string Pattern { get; set; } = "{{ Model.ContentItem.DisplayText | slugify }}"; +public class AliasPartSettings +{ + [DefaultValue("{{ Model.ContentItem.DisplayText | slugify }}")] + public string Pattern { get; set; } = "{{ Model.ContentItem.DisplayText | slugify }}"; - /// - /// Gets or sets whether a user can define a custom alias. - /// - [DefaultValue(AliasPartOptions.Editable)] - public AliasPartOptions Options { get; set; } = AliasPartOptions.Editable; - } + /// + /// Gets or sets whether a user can define a custom alias. + /// + [DefaultValue(AliasPartOptions.Editable)] + public AliasPartOptions Options { get; set; } = AliasPartOptions.Editable; } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsDisplayDriver.cs index 7c8ddf74974..ed68168ce4f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsDisplayDriver.cs @@ -7,56 +7,55 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Liquid; -namespace OrchardCore.Alias.Settings +namespace OrchardCore.Alias.Settings; + +public sealed class AliasPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class AliasPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly ILiquidTemplateManager _templateManager; + private readonly ILiquidTemplateManager _templateManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AliasPartSettingsDisplayDriver( - ILiquidTemplateManager templateManager, - IStringLocalizer localizer) - { - _templateManager = templateManager; - S = localizer; - } + public AliasPartSettingsDisplayDriver( + ILiquidTemplateManager templateManager, + IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("AliasPartSettings_Edit", model => { - return Initialize("AliasPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.Pattern = settings.Pattern; - model.Options = settings.Options; - model.AliasPartSettings = settings; - }).Location("Content"); - } + model.Pattern = settings.Pattern; + model.Options = settings.Options; + model.AliasPartSettings = settings; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new AliasPartSettingsViewModel(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new AliasPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Pattern, - m => m.Options); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Pattern, + m => m.Options); - if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) - { - context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else + if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) + { + context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + else + { + context.Builder.WithSettings(new AliasPartSettings { - context.Builder.WithSettings(new AliasPartSettings - { - Pattern = model.Pattern, - Options = model.Options, - }); - } - - return Edit(contentTypePartDefinition, context); + Pattern = model.Pattern, + Options = model.Options, + }); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsViewModel.cs index 55e8dfb5868..df40019ca54 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Settings/AliasPartSettingsViewModel.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Alias.Settings +namespace OrchardCore.Alias.Settings; + +public class AliasPartSettingsViewModel { - public class AliasPartSettingsViewModel - { - public string Pattern { get; set; } - public AliasPartOptions Options { get; set; } - [BindNever] - public AliasPartSettings AliasPartSettings { get; set; } - } + public string Pattern { get; set; } + public AliasPartOptions Options { get; set; } + [BindNever] + public AliasPartSettings AliasPartSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Alias/Startup.cs index f04e959f197..36f54299b5b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/Startup.cs @@ -20,56 +20,55 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.Alias +namespace OrchardCore.Alias; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => + o.MemberAccessStrategy.Register(); + + o.MemberAccessStrategy.Register("Alias", (obj, context) => { - o.MemberAccessStrategy.Register(); + var liquidTemplateContext = (LiquidTemplateContext)context; - o.MemberAccessStrategy.Register("Alias", (obj, context) => + return new LiquidPropertyAccessor(liquidTemplateContext, async (alias, context) => { - var liquidTemplateContext = (LiquidTemplateContext)context; - - return new LiquidPropertyAccessor(liquidTemplateContext, async (alias, context) => - { - var session = context.Services.GetRequiredService(); + var session = context.Services.GetRequiredService(); #pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons - var contentItem = await session.Query(x => - x.Published && x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync(); + var contentItem = await session.Query(x => + x.Published && x.Alias == alias.ToLowerInvariant()).FirstOrDefaultAsync(); #pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons - if (contentItem == null) - { - return NilValue.Instance; - } + if (contentItem == null) + { + return NilValue.Instance; + } - var contentManager = context.Services.GetRequiredService(); - contentItem = await contentManager.LoadAsync(contentItem); + var contentManager = context.Services.GetRequiredService(); + contentItem = await contentManager.LoadAsync(contentItem); - return new ObjectValue(contentItem); - }); + return new ObjectValue(contentItem); }); }); + }); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); - services.AddDataMigration(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); - // Identity Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Identity Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Alias/ViewModels/AliasPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Alias/ViewModels/AliasPartViewModel.cs index b303a689c5f..8fd3f279941 100644 --- a/src/OrchardCore.Modules/OrchardCore.Alias/ViewModels/AliasPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Alias/ViewModels/AliasPartViewModel.cs @@ -3,19 +3,18 @@ using OrchardCore.Alias.Settings; using OrchardCore.ContentManagement; -namespace OrchardCore.Alias.ViewModels +namespace OrchardCore.Alias.ViewModels; + +public class AliasPartViewModel { - public class AliasPartViewModel - { - public string Alias { get; set; } + public string Alias { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public AliasPart AliasPart { get; set; } + [BindNever] + public AliasPart AliasPart { get; set; } - [BindNever] - public AliasPartSettings Settings { get; set; } - } + [BindNever] + public AliasPartSettings Settings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/AdminMenu.cs index 1d7749194fb..25a0d7bd706 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/AdminMenu.cs @@ -2,34 +2,33 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["GraphiQL"], S["GraphiQL"].PrefixPosition(), graphiQL => graphiQL - .Action("Index", "Admin", "OrchardCore.Apis.GraphQL") - .Permission(CommonPermissions.ExecuteGraphQL) - .LocalNav() - ) - ); + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["GraphiQL"], S["GraphiQL"].PrefixPosition(), graphiQL => graphiQL + .Action("Index", "Admin", "OrchardCore.Apis.GraphQL") + .Permission(CommonPermissions.ExecuteGraphQL) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Controllers/AdminController.cs index a2d7c115910..c4ad09d5536 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Controllers/AdminController.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc; using OrchardCore.Admin; -namespace OrchardCore.Apis.GraphQL.Controllers +namespace OrchardCore.Apis.GraphQL.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + [HttpGet] + [Admin("GraphQL", "GraphQL")] + public IActionResult Index() { - [HttpGet] - [Admin("GraphQL", "GraphQL")] - public IActionResult Index() - { - return View(); - } + return View(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Extensions/DocumentWriterExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Extensions/DocumentWriterExtensions.cs index 1163977934a..2bcc26dca0d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Extensions/DocumentWriterExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Extensions/DocumentWriterExtensions.cs @@ -5,32 +5,31 @@ using GraphQL; using Microsoft.AspNetCore.Http; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +internal static class DocumentWriterExtensions { - internal static class DocumentWriterExtensions + public static async Task WriteErrorAsync(this IGraphQLSerializer graphQLSerializer, HttpContext context, string message, Exception e = null) { - public static async Task WriteErrorAsync(this IGraphQLSerializer graphQLSerializer, HttpContext context, string message, Exception e = null) - { - ArgumentNullException.ThrowIfNull(message); + ArgumentNullException.ThrowIfNull(message); - var errorResult = new ExecutionResult - { - Errors = [] - }; + var errorResult = new ExecutionResult + { + Errors = [] + }; - if (e == null) - { - errorResult.Errors.Add(new ExecutionError(message)); - } - else - { - errorResult.Errors.Add(new ExecutionError(message, e)); - } + if (e == null) + { + errorResult.Errors.Add(new ExecutionError(message)); + } + else + { + errorResult.Errors.Add(new ExecutionError(message, e)); + } - context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - context.Response.ContentType = MediaTypeNames.Application.Json; + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.ContentType = MediaTypeNames.Application.Json; - await graphQLSerializer.WriteAsync(context.Response.Body, errorResult); - } + await graphQLSerializer.WriteAsync(context.Response.Body, errorResult); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs index 83d167d995c..d6483af0908 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLMiddleware.cs @@ -23,188 +23,187 @@ using OrchardCore.Apis.GraphQL.ValidationRules; using OrchardCore.Routing; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +public class GraphQLMiddleware : IMiddleware { - public class GraphQLMiddleware : IMiddleware + private readonly ILogger _logger; + private readonly GraphQLSettings _settings; + private readonly IGraphQLTextSerializer _graphQLTextSerializer; + private readonly IGraphQLSerializer _serializer; + private readonly IDocumentExecuter _executer; + internal static readonly Encoding _utf8Encoding = new UTF8Encoding(false); + private static readonly MediaType _jsonMediaType = new("application/json"); + private static readonly MediaType _graphQlMediaType = new("application/graphql"); + + public GraphQLMiddleware( + IOptions settingsOption, + IDocumentExecuter executer, + IGraphQLSerializer serializer, + IGraphQLTextSerializer graphQLTextSerializer, + ILogger logger) + { + _settings = settingsOption.Value; + _executer = executer; + _serializer = serializer; + _graphQLTextSerializer = graphQLTextSerializer; + _logger = logger; + } + public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - private readonly ILogger _logger; - private readonly GraphQLSettings _settings; - private readonly IGraphQLTextSerializer _graphQLTextSerializer; - private readonly IGraphQLSerializer _serializer; - private readonly IDocumentExecuter _executer; - internal static readonly Encoding _utf8Encoding = new UTF8Encoding(false); - private static readonly MediaType _jsonMediaType = new("application/json"); - private static readonly MediaType _graphQlMediaType = new("application/graphql"); - - public GraphQLMiddleware( - IOptions settingsOption, - IDocumentExecuter executer, - IGraphQLSerializer serializer, - IGraphQLTextSerializer graphQLTextSerializer, - ILogger logger) + if (!IsGraphQLRequest(context)) { - _settings = settingsOption.Value; - _executer = executer; - _serializer = serializer; - _graphQLTextSerializer = graphQLTextSerializer; - _logger = logger; + await next(context); } - public async Task InvokeAsync(HttpContext context, RequestDelegate next) + else { - if (!IsGraphQLRequest(context)) + var authenticationService = context.RequestServices.GetService(); + var authenticateResult = await authenticationService.AuthenticateAsync(context, "Api"); + if (authenticateResult.Succeeded) { - await next(context); + context.User = authenticateResult.Principal; + } + var authorizationService = context.RequestServices.GetService(); + var authorized = await authorizationService.AuthorizeAsync(context.User, CommonPermissions.ExecuteGraphQL); + + if (authorized) + { + await ExecuteAsync(context); } else { - var authenticationService = context.RequestServices.GetService(); - var authenticateResult = await authenticationService.AuthenticateAsync(context, "Api"); - if (authenticateResult.Succeeded) - { - context.User = authenticateResult.Principal; - } - var authorizationService = context.RequestServices.GetService(); - var authorized = await authorizationService.AuthorizeAsync(context.User, CommonPermissions.ExecuteGraphQL); - - if (authorized) - { - await ExecuteAsync(context); - } - else - { - await context.ChallengeAsync("Api"); - } + await context.ChallengeAsync("Api"); } } - private bool IsGraphQLRequest(HttpContext context) - { - return context.Request.Path.StartsWithNormalizedSegments(_settings.Path, StringComparison.OrdinalIgnoreCase); - } + } + private bool IsGraphQLRequest(HttpContext context) + { + return context.Request.Path.StartsWithNormalizedSegments(_settings.Path, StringComparison.OrdinalIgnoreCase); + } - private async Task ExecuteAsync(HttpContext context) - { - GraphQLNamedQueryRequest request = null; + private async Task ExecuteAsync(HttpContext context) + { + GraphQLNamedQueryRequest request = null; - // c.f. https://graphql.org/learn/serving-over-http/#post-request + // c.f. https://graphql.org/learn/serving-over-http/#post-request - try + try + { + if (HttpMethods.IsPost(context.Request.Method)) { - if (HttpMethods.IsPost(context.Request.Method)) - { - var mediaType = new MediaType(context.Request.ContentType); + var mediaType = new MediaType(context.Request.ContentType); - if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType)) + if (mediaType.IsSubsetOf(_jsonMediaType) || mediaType.IsSubsetOf(_graphQlMediaType)) + { + using var sr = new StreamReader(context.Request.Body); + if (mediaType.IsSubsetOf(_graphQlMediaType)) { - using var sr = new StreamReader(context.Request.Body); - if (mediaType.IsSubsetOf(_graphQlMediaType)) - { - request = new GraphQLNamedQueryRequest - { - Query = await sr.ReadToEndAsync() - }; - } - else + request = new GraphQLNamedQueryRequest { - request = _graphQLTextSerializer.Deserialize(await sr.ReadToEndAsync()); - } + Query = await sr.ReadToEndAsync() + }; } else { - request = CreateRequestFromQueryString(context); + request = _graphQLTextSerializer.Deserialize(await sr.ReadToEndAsync()); } } - else if (HttpMethods.IsGet(context.Request.Method)) - { - request = CreateRequestFromQueryString(context, true); - } - - if (request == null) + else { - throw new InvalidOperationException("Unable to create a graphqlrequest from this request"); + request = CreateRequestFromQueryString(context); } } - catch (Exception e) + else if (HttpMethods.IsGet(context.Request.Method)) { - await _serializer.WriteErrorAsync(context, "An error occurred while processing the GraphQL query", e); - _logger.LogError(e, "An error occurred while processing the GraphQL query."); + request = CreateRequestFromQueryString(context, true); + } - return; + if (request == null) + { + throw new InvalidOperationException("Unable to create a graphqlrequest from this request"); } + } + catch (Exception e) + { + await _serializer.WriteErrorAsync(context, "An error occurred while processing the GraphQL query", e); + _logger.LogError(e, "An error occurred while processing the GraphQL query."); - var queryToExecute = request.Query; + return; + } - if (!string.IsNullOrEmpty(request.NamedQuery)) - { - var namedQueries = context.RequestServices.GetServices(); + var queryToExecute = request.Query; - var queries = namedQueries - .SelectMany(dict => dict.Resolve()) - .ToDictionary(pair => pair.Key, pair => pair.Value); + if (!string.IsNullOrEmpty(request.NamedQuery)) + { + var namedQueries = context.RequestServices.GetServices(); - queryToExecute = queries[request.NamedQuery]; - } + var queries = namedQueries + .SelectMany(dict => dict.Resolve()) + .ToDictionary(pair => pair.Key, pair => pair.Value); - var schemaService = context.RequestServices.GetService(); - var schema = await schemaService.GetSchemaAsync(); - var dataLoaderDocumentListener = context.RequestServices.GetRequiredService(); - var result = await _executer.ExecuteAsync(options => - { - options.Schema = schema; - options.Query = queryToExecute; - options.OperationName = request.OperationName; - options.Variables = request.Variables; - options.UserContext = _settings.BuildUserContext?.Invoke(context); - options.ValidationRules = DocumentValidator.CoreRules - .Concat(context.RequestServices.GetServices()) - .Append(new ComplexityValidationRule(new ComplexityConfiguration - { - MaxDepth = _settings.MaxDepth, - MaxComplexity = _settings.MaxComplexity, - FieldImpact = _settings.FieldImpact - })); - options.Listeners.Add(dataLoaderDocumentListener); - options.RequestServices = context.RequestServices; - }); - - context.Response.StatusCode = (int)(result.Errors == null || result.Errors.Count == 0 - ? HttpStatusCode.OK - : result.Errors.Any(x => x is ValidationError ve && ve.Number == RequiresPermissionValidationRule.ErrorCode) - ? HttpStatusCode.Unauthorized - : HttpStatusCode.BadRequest); - - context.Response.ContentType = MediaTypeNames.Application.Json; - - await _serializer.WriteAsync(context.Response.Body, result); + queryToExecute = queries[request.NamedQuery]; } - private GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false) + var schemaService = context.RequestServices.GetService(); + var schema = await schemaService.GetSchemaAsync(); + var dataLoaderDocumentListener = context.RequestServices.GetRequiredService(); + var result = await _executer.ExecuteAsync(options => { - if (!context.Request.Query.ContainsKey("query")) - { - if (validateQueryKey) + options.Schema = schema; + options.Query = queryToExecute; + options.OperationName = request.OperationName; + options.Variables = request.Variables; + options.UserContext = _settings.BuildUserContext?.Invoke(context); + options.ValidationRules = DocumentValidator.CoreRules + .Concat(context.RequestServices.GetServices()) + .Append(new ComplexityValidationRule(new ComplexityConfiguration { - throw new InvalidOperationException("The 'query' query string parameter is missing"); - } + MaxDepth = _settings.MaxDepth, + MaxComplexity = _settings.MaxComplexity, + FieldImpact = _settings.FieldImpact + })); + options.Listeners.Add(dataLoaderDocumentListener); + options.RequestServices = context.RequestServices; + }); + + context.Response.StatusCode = (int)(result.Errors == null || result.Errors.Count == 0 + ? HttpStatusCode.OK + : result.Errors.Any(x => x is ValidationError ve && ve.Number == RequiresPermissionValidationRule.ErrorCode) + ? HttpStatusCode.Unauthorized + : HttpStatusCode.BadRequest); + + context.Response.ContentType = MediaTypeNames.Application.Json; + + await _serializer.WriteAsync(context.Response.Body, result); + } - return null; + private GraphQLNamedQueryRequest CreateRequestFromQueryString(HttpContext context, bool validateQueryKey = false) + { + if (!context.Request.Query.ContainsKey("query")) + { + if (validateQueryKey) + { + throw new InvalidOperationException("The 'query' query string parameter is missing"); } - var request = new GraphQLNamedQueryRequest - { - Query = context.Request.Query["query"] - }; + return null; + } - if (context.Request.Query.ContainsKey("variables")) - { - request.Variables = _graphQLTextSerializer.Deserialize(context.Request.Query["variables"]); - } + var request = new GraphQLNamedQueryRequest + { + Query = context.Request.Query["query"] + }; - if (context.Request.Query.ContainsKey("operationName")) - { - request.OperationName = context.Request.Query["operationName"]; - } + if (context.Request.Query.ContainsKey("variables")) + { + request.Variables = _graphQLTextSerializer.Deserialize(context.Request.Query["variables"]); + } - return request; + if (context.Request.Query.ContainsKey("operationName")) + { + request.OperationName = context.Request.Query["operationName"]; } + + return request; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLNamedQueryRequest.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLNamedQueryRequest.cs index 146b17b1b98..6e44c520cc5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLNamedQueryRequest.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/GraphQLNamedQueryRequest.cs @@ -2,14 +2,13 @@ using GraphQL.Transport; using OrchardCore.Apis.GraphQL.Queries; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +public class GraphQLNamedQueryRequest : GraphQLRequest { - public class GraphQLNamedQueryRequest : GraphQLRequest - { - /// - /// Used to store some graphql query on the server, and then the client only needs to submit the name of that query to reduce the size of the network request - /// - /// - public string NamedQuery { get; set; } - } + /// + /// Used to store some graphql query on the server, and then the client only needs to submit the name of that query to reduce the size of the network request + /// + /// + public string NamedQuery { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/OrchardFieldNameConverter.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/OrchardFieldNameConverter.cs index b8a7bc27986..4ce6a657d53 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/OrchardFieldNameConverter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/OrchardFieldNameConverter.cs @@ -2,35 +2,34 @@ using GraphQL.Conversion; using GraphQL.Types; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +public class OrchardFieldNameConverter : INameConverter { - public class OrchardFieldNameConverter : INameConverter + private readonly CamelCaseNameConverter _defaultConverter = new(); + + // todo: custom argument name? + public string NameForArgument(string argumentName, IComplexGraphType parentGraphType, FieldType field) { - private readonly CamelCaseNameConverter _defaultConverter = new(); + return _defaultConverter.NameForArgument(argumentName, parentGraphType, field); + } - // todo: custom argument name? - public string NameForArgument(string argumentName, IComplexGraphType parentGraphType, FieldType field) - { - return _defaultConverter.NameForArgument(argumentName, parentGraphType, field); - } + // TODO: check functionality. + public string NameForField(string fieldName, IComplexGraphType parentGraphType) + { + var attributes = parentGraphType?.GetType().GetCustomAttributes(typeof(GraphQLFieldNameAttribute), true); - // TODO: check functionality. - public string NameForField(string fieldName, IComplexGraphType parentGraphType) + if (attributes != null) { - var attributes = parentGraphType?.GetType().GetCustomAttributes(typeof(GraphQLFieldNameAttribute), true); - - if (attributes != null) + foreach (var attribute in attributes.Cast()) { - foreach (var attribute in attributes.Cast()) + if (attribute.Field == fieldName) { - if (attribute.Field == fieldName) - { - return attribute.Mapped; - } + return attribute.Mapped; } } - - return _defaultConverter.NameForField(fieldName, parentGraphType); } + + return _defaultConverter.NameForField(fieldName, parentGraphType); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs index b0be8eb9661..f3e24912370 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Services/SchemaService.cs @@ -8,27 +8,45 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.Environment.Shell.Scope; -namespace OrchardCore.Apis.GraphQL.Services +namespace OrchardCore.Apis.GraphQL.Services; + +public class SchemaService : ISchemaFactory { - public class SchemaService : ISchemaFactory + private readonly IEnumerable _schemaBuilders; + private readonly IServiceProvider _serviceProvider; + private readonly SemaphoreSlim _schemaGenerationSemaphore = new(1, 1); + private readonly ConcurrentDictionary _identifiers = new(); + + private ISchema _schema; + + public SchemaService(IEnumerable schemaBuilders, IServiceProvider serviceProvider) { - private readonly IEnumerable _schemaBuilders; - private readonly IServiceProvider _serviceProvider; - private readonly SemaphoreSlim _schemaGenerationSemaphore = new(1, 1); - private readonly ConcurrentDictionary _identifiers = new(); + _schemaBuilders = schemaBuilders; + _serviceProvider = serviceProvider; + } - private ISchema _schema; + public async Task GetSchemaAsync() + { + var hasChanged = false; - public SchemaService(IEnumerable schemaBuilders, IServiceProvider serviceProvider) + foreach (var builder in _schemaBuilders) { - _schemaBuilders = schemaBuilders; - _serviceProvider = serviceProvider; + if (_identifiers.TryGetValue(builder, out var identifier) && await builder.GetIdentifierAsync() != identifier) + { + hasChanged = true; + break; + } } - public async Task GetSchemaAsync() + if (_schema is object && !hasChanged) { - var hasChanged = false; + return _schema; + } + + await _schemaGenerationSemaphore.WaitAsync(); + try + { foreach (var builder in _schemaBuilders) { if (_identifiers.TryGetValue(builder, out var identifier) && await builder.GetIdentifierAsync() != identifier) @@ -43,83 +61,64 @@ public async Task GetSchemaAsync() return _schema; } - await _schemaGenerationSemaphore.WaitAsync(); + var serviceProvider = ShellScope.Services; - try + var schema = new Schema(new SelfActivatingServiceProvider(_serviceProvider)) { - foreach (var builder in _schemaBuilders) - { - if (_identifiers.TryGetValue(builder, out var identifier) && await builder.GetIdentifierAsync() != identifier) - { - hasChanged = true; - break; - } - } - - if (_schema is object && !hasChanged) - { - return _schema; - } - - var serviceProvider = ShellScope.Services; + Query = new ObjectGraphType { Name = "Query" }, + Mutation = new ObjectGraphType { Name = "Mutation" }, + Subscription = new ObjectGraphType { Name = "Subscription" }, + NameConverter = new OrchardFieldNameConverter(), + }; - var schema = new Schema(new SelfActivatingServiceProvider(_serviceProvider)) - { - Query = new ObjectGraphType { Name = "Query" }, - Mutation = new ObjectGraphType { Name = "Mutation" }, - Subscription = new ObjectGraphType { Name = "Subscription" }, - NameConverter = new OrchardFieldNameConverter(), - }; + foreach (var type in serviceProvider.GetServices()) + { + schema.RegisterType(type); + } - foreach (var type in serviceProvider.GetServices()) - { - schema.RegisterType(type); - } + foreach (var type in serviceProvider.GetServices()) + { + schema.RegisterType(type); + } - foreach (var type in serviceProvider.GetServices()) - { - schema.RegisterType(type); - } + foreach (var builder in _schemaBuilders) + { + var identifier = await builder.GetIdentifierAsync(); - foreach (var builder in _schemaBuilders) + // Null being a valid value not yet updated. + if (identifier != string.Empty) { - var identifier = await builder.GetIdentifierAsync(); - - // Null being a valid value not yet updated. - if (identifier != string.Empty) - { - _identifiers[builder] = identifier; - } - - await builder.BuildAsync(schema); + _identifiers[builder] = identifier; } + await builder.BuildAsync(schema); + } - // Clean Query, Mutation and Subscription if they have no fields - // to prevent GraphQL configuration errors. - - if (schema.Query.Fields.Count == 0) - { - schema.Query = null; - } - if (schema.Mutation.Fields.Count == 0) - { - schema.Mutation = null; - } + // Clean Query, Mutation and Subscription if they have no fields + // to prevent GraphQL configuration errors. - if (schema.Subscription.Fields.Count == 0) - { - schema.Subscription = null; - } + if (schema.Query.Fields.Count == 0) + { + schema.Query = null; + } - schema.Initialize(); - return _schema = schema; + if (schema.Mutation.Fields.Count == 0) + { + schema.Mutation = null; } - finally + + if (schema.Subscription.Fields.Count == 0) { - _schemaGenerationSemaphore.Release(); + schema.Subscription = null; } + + schema.Initialize(); + return _schema = schema; + } + finally + { + _schemaGenerationSemaphore.Release(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs index 1115d8b45e2..9248a17a326 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/Startup.cs @@ -19,77 +19,76 @@ using OrchardCore.Navigation; using OrchardCore.Security.Permissions; -namespace OrchardCore.Apis.GraphQL +namespace OrchardCore.Apis.GraphQL; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly IHostEnvironment _hostingEnvironment; + + public Startup(IHostEnvironment hostingEnvironment) { - private readonly IHostEnvironment _hostingEnvironment; + _hostingEnvironment = hostingEnvironment; + } - public Startup(IHostEnvironment hostingEnvironment) - { - _hostingEnvironment = hostingEnvironment; - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); - public override void ConfigureServices(IServiceCollection services) + services.AddSingleton(services => { - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + var settings = services.GetRequiredService>(); + return new ErrorInfoProvider(new ErrorInfoProviderOptions { ExposeExceptionDetails = settings.Value.ExposeExceptions }); + }); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddTransient(); + services.AddSingleton(); - services.AddSingleton(services => - { - var settings = services.GetRequiredService>(); - return new ErrorInfoProvider(new ErrorInfoProviderOptions { ExposeExceptionDetails = settings.Value.ExposeExceptions }); - }); + services.AddGraphQL(builder => builder.AddSystemTextJson((options, sp) => + { + // Common types of converters are already configured in the assembly "GraphQL.SystemTextJson". + options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); - services.AddScoped(); - services.AddTransient(); - services.AddSingleton(); + var documentJsonSerializerOptions = sp.GetRequiredService>().Value; + options.Merge(documentJsonSerializerOptions.SerializerOptions); + })); - services.AddGraphQL(builder => builder.AddSystemTextJson((options, sp) => - { - // Common types of converters are already configured in the assembly "GraphQL.SystemTextJson". - options.Converters.Add(GraphQLNamedQueryRequestJsonConverter.Instance); + services.AddOptions().Configure((c, configuration) => + { + var exposeExceptions = configuration.GetValue( + $"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.ExposeExceptions)}", + _hostingEnvironment.IsDevelopment()); - var documentJsonSerializerOptions = sp.GetRequiredService>().Value; - options.Merge(documentJsonSerializerOptions.SerializerOptions); - })); + var maxNumberOfResultsValidationMode = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResultsValidationMode)}") + ?? MaxNumberOfResultsValidationMode.Default; - services.AddOptions().Configure((c, configuration) => + if (maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Default) { - var exposeExceptions = configuration.GetValue( - $"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.ExposeExceptions)}", - _hostingEnvironment.IsDevelopment()); - - var maxNumberOfResultsValidationMode = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResultsValidationMode)}") - ?? MaxNumberOfResultsValidationMode.Default; - - if (maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Default) - { - maxNumberOfResultsValidationMode = _hostingEnvironment.IsDevelopment() ? MaxNumberOfResultsValidationMode.Enabled : MaxNumberOfResultsValidationMode.Disabled; - } + maxNumberOfResultsValidationMode = _hostingEnvironment.IsDevelopment() ? MaxNumberOfResultsValidationMode.Enabled : MaxNumberOfResultsValidationMode.Disabled; + } - c.BuildUserContext = ctx => new GraphQLUserContext - { - User = ctx.User, - }; - c.ExposeExceptions = exposeExceptions; - c.MaxDepth = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxDepth)}") ?? 100; - c.MaxComplexity = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxComplexity)}"); - c.FieldImpact = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.FieldImpact)}"); - c.MaxNumberOfResults = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResults)}") ?? 1000; - c.MaxNumberOfResultsValidationMode = maxNumberOfResultsValidationMode; - c.DefaultNumberOfResults = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.DefaultNumberOfResults)}") ?? 100; - }); - } + c.BuildUserContext = ctx => new GraphQLUserContext + { + User = ctx.User, + }; + c.ExposeExceptions = exposeExceptions; + c.MaxDepth = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxDepth)}") ?? 100; + c.MaxComplexity = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxComplexity)}"); + c.FieldImpact = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.FieldImpact)}"); + c.MaxNumberOfResults = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.MaxNumberOfResults)}") ?? 1000; + c.MaxNumberOfResultsValidationMode = maxNumberOfResultsValidationMode; + c.DefaultNumberOfResults = configuration.GetValue($"OrchardCore_Apis_GraphQL:{nameof(GraphQLSettings.DefaultNumberOfResults)}") ?? 100; + }); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseMiddleware(); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseMiddleware(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/MaxNumberOfResultsValidationRule.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/MaxNumberOfResultsValidationRule.cs index 8f3ce387f3d..435275672b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/MaxNumberOfResultsValidationRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/MaxNumberOfResultsValidationRule.cs @@ -5,69 +5,68 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace OrchardCore.Apis.GraphQL.ValidationRules +namespace OrchardCore.Apis.GraphQL.ValidationRules; + +public class MaxNumberOfResultsValidationRule : IValidationRule { - public class MaxNumberOfResultsValidationRule : IValidationRule - { - private readonly int _maxNumberOfResults; - private readonly MaxNumberOfResultsValidationMode _maxNumberOfResultsValidationMode; - protected readonly IStringLocalizer S; - private readonly ILogger _logger; + private readonly int _maxNumberOfResults; + private readonly MaxNumberOfResultsValidationMode _maxNumberOfResultsValidationMode; + protected readonly IStringLocalizer S; + private readonly ILogger _logger; - public MaxNumberOfResultsValidationRule( - IOptions options, - IStringLocalizer localizer, - ILogger logger) - { - var settings = options.Value; - _maxNumberOfResults = settings.MaxNumberOfResults; - _maxNumberOfResultsValidationMode = settings.MaxNumberOfResultsValidationMode; - S = localizer; - _logger = logger; - } + public MaxNumberOfResultsValidationRule( + IOptions options, + IStringLocalizer localizer, + ILogger logger) + { + var settings = options.Value; + _maxNumberOfResults = settings.MaxNumberOfResults; + _maxNumberOfResultsValidationMode = settings.MaxNumberOfResultsValidationMode; + S = localizer; + _logger = logger; + } - public ValueTask ValidateAsync(ValidationContext validationContext) + public ValueTask ValidateAsync(ValidationContext validationContext) + { + return ValueTask.FromResult((INodeVisitor)new NodeVisitors( + new MatchingNodeVisitor((arg, visitorContext) => { - return ValueTask.FromResult((INodeVisitor)new NodeVisitors( - new MatchingNodeVisitor((arg, visitorContext) => + if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null) { - if ((arg.Name == "first" || arg.Name == "last") && arg.Value != null) - { - var context = (GraphQLUserContext)validationContext.UserContext; + var context = (GraphQLUserContext)validationContext.UserContext; - int? value = null; + int? value = null; - if (arg.Value is GraphQLIntValue) + if (arg.Value is GraphQLIntValue) + { + value = int.Parse((arg.Value as GraphQLIntValue).Value); + } + else + { + if (validationContext.Variables.TryGetValue(arg.Value.ToString(), out var input)) { - value = int.Parse((arg.Value as GraphQLIntValue).Value); + value = (int?)input; } - else + } + + if (value.HasValue && value > _maxNumberOfResults) + { + if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled) { - if (validationContext.Variables.TryGetValue(arg.Value.ToString(), out var input)) - { - value = (int?)input; - } + validationContext.ReportError(new ValidationError( + validationContext.Document.Source, + "ArgumentInputError", + S["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults], + arg)); } - - if (value.HasValue && value > _maxNumberOfResults) + else { - if (_maxNumberOfResultsValidationMode == MaxNumberOfResultsValidationMode.Enabled) - { - validationContext.ReportError(new ValidationError( - validationContext.Document.Source, - "ArgumentInputError", - S["'{0}' exceeds the maximum number of results for '{1}' ({2})", value.Value, arg.Name, _maxNumberOfResults], - arg)); - } - else - { - _logger.LogInformation("'{value}' exceeds the maximum number of results for '{name}' ({total})", value.Value, arg.Name, _maxNumberOfResults); + _logger.LogInformation("'{value}' exceeds the maximum number of results for '{name}' ({total})", value.Value, arg.Name, _maxNumberOfResults); - arg = new GraphQLArgument(arg.Name, new GraphQLIntValue(_maxNumberOfResults)); // if disabled mode we just log info and override the arg to be maxvalue - } + arg = new GraphQLArgument(arg.Name, new GraphQLIntValue(_maxNumberOfResults)); // if disabled mode we just log info and override the arg to be maxvalue } } - }))); - } + } + }))); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/RequiresPermissionValidationRule.cs b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/RequiresPermissionValidationRule.cs index 8766d949501..729943e36d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/RequiresPermissionValidationRule.cs +++ b/src/OrchardCore.Modules/OrchardCore.Apis.GraphQL/ValidationRules/RequiresPermissionValidationRule.cs @@ -8,117 +8,116 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Localization; -namespace OrchardCore.Apis.GraphQL.ValidationRules +namespace OrchardCore.Apis.GraphQL.ValidationRules; + +public class RequiresPermissionValidationRule : IValidationRule { - public class RequiresPermissionValidationRule : IValidationRule - { - public static readonly string ErrorCode = "Unauthorized"; - private readonly IAuthorizationService _authorizationService; - protected readonly IStringLocalizer S; + public static readonly string ErrorCode = "Unauthorized"; + private readonly IAuthorizationService _authorizationService; + protected readonly IStringLocalizer S; - public RequiresPermissionValidationRule( - IAuthorizationService authorizationService, - IStringLocalizer localizer) - { - _authorizationService = authorizationService; - S = localizer; - } + public RequiresPermissionValidationRule( + IAuthorizationService authorizationService, + IStringLocalizer localizer) + { + _authorizationService = authorizationService; + S = localizer; + } - public async ValueTask ValidateAsync(ValidationContext validationContext) - { - // shouldn't we access UserContext from validation-context inside MatchingNodeVisitor actions? - var userContext = (GraphQLUserContext)validationContext.UserContext; + public async ValueTask ValidateAsync(ValidationContext validationContext) + { + // shouldn't we access UserContext from validation-context inside MatchingNodeVisitor actions? + var userContext = (GraphQLUserContext)validationContext.UserContext; - return await Task.FromResult(new NodeVisitors( - new MatchingNodeVisitor(async (operationDefinition, validationContext) => - { - await AuthorizeOperationAsync(operationDefinition, validationContext, userContext, operationDefinition.Operation, operationDefinition?.Name?.StringValue); - }), - new MatchingNodeVisitor(async (objectFieldAst, validationContext) => - { - if (validationContext.TypeInfo.GetArgument()?.ResolvedType.GetNamedType() is IComplexGraphType argumentType) - { - var fieldType = argumentType.GetField(objectFieldAst.Name); - await AuthorizeNodePermissionAsync(objectFieldAst, fieldType, validationContext, userContext); - } - }), - new MatchingNodeVisitor(async (fieldAst, validationContext) => + return await Task.FromResult(new NodeVisitors( + new MatchingNodeVisitor(async (operationDefinition, validationContext) => + { + await AuthorizeOperationAsync(operationDefinition, validationContext, userContext, operationDefinition.Operation, operationDefinition?.Name?.StringValue); + }), + new MatchingNodeVisitor(async (objectFieldAst, validationContext) => + { + if (validationContext.TypeInfo.GetArgument()?.ResolvedType.GetNamedType() is IComplexGraphType argumentType) { - var fieldDef = validationContext.TypeInfo.GetFieldDef(); + var fieldType = argumentType.GetField(objectFieldAst.Name); + await AuthorizeNodePermissionAsync(objectFieldAst, fieldType, validationContext, userContext); + } + }), + new MatchingNodeVisitor(async (fieldAst, validationContext) => + { + var fieldDef = validationContext.TypeInfo.GetFieldDef(); - if (fieldDef == null) - return; + if (fieldDef == null) + return; - // check target field - await AuthorizeNodePermissionAsync(fieldAst, fieldDef, validationContext, userContext); - // check returned graph type - // AuthorizeNodePermissionAsync(fieldAst, fieldDef.ResolvedType.GetNamedType(), validationContext, userContext).GetAwaiter().GetResult(); // TODO: need to think of something to avoid this - }) - )); - } + // check target field + await AuthorizeNodePermissionAsync(fieldAst, fieldDef, validationContext, userContext); + // check returned graph type + // AuthorizeNodePermissionAsync(fieldAst, fieldDef.ResolvedType.GetNamedType(), validationContext, userContext).GetAwaiter().GetResult(); // TODO: need to think of something to avoid this + }) + )); + } - private async Task AuthorizeOperationAsync(ASTNode node, ValidationContext validationContext, GraphQLUserContext userContext, OperationType? operationType, string operationName) + private async Task AuthorizeOperationAsync(ASTNode node, ValidationContext validationContext, GraphQLUserContext userContext, OperationType? operationType, string operationName) + { + if (operationType == OperationType.Mutation && !(await _authorizationService.AuthorizeAsync(userContext.User, CommonPermissions.ExecuteGraphQLMutations))) { - if (operationType == OperationType.Mutation && !(await _authorizationService.AuthorizeAsync(userContext.User, CommonPermissions.ExecuteGraphQLMutations))) - { - validationContext.ReportError(new ValidationError( - validationContext.Document.Source, - ErrorCode, - S["Authorization is required to access {0}.", operationName], - node)); - } + validationContext.ReportError(new ValidationError( + validationContext.Document.Source, + ErrorCode, + S["Authorization is required to access {0}.", operationName], + node)); } + } + + private async Task AuthorizeNodePermissionAsync(ASTNode node, FieldType fieldType, ValidationContext validationContext, GraphQLUserContext userContext) + { + var permissions = fieldType?.GetPermissions(); - private async Task AuthorizeNodePermissionAsync(ASTNode node, FieldType fieldType, ValidationContext validationContext, GraphQLUserContext userContext) + if (permissions == null) { - var permissions = fieldType?.GetPermissions(); + return; + } - if (permissions == null) - { - return; - } + var totalPermissions = permissions.Count(); - var totalPermissions = permissions.Count(); + if (totalPermissions == 0) + { + return; + } - if (totalPermissions == 0) + if (totalPermissions == 1) + { + var permission = permissions.First(); + // small optimization for the single policy - no 'new List<>()', no 'await Task.WhenAll()' + if (!await _authorizationService.AuthorizeAsync(userContext.User, permission.Permission, permission.Resource)) { - return; + AddPermissionValidationError(validationContext, node, fieldType.Name); } + } + else + { + var tasks = new List>(); - if (totalPermissions == 1) + foreach (var permission in permissions) { - var permission = permissions.First(); - // small optimization for the single policy - no 'new List<>()', no 'await Task.WhenAll()' - if (!await _authorizationService.AuthorizeAsync(userContext.User, permission.Permission, permission.Resource)) - { - AddPermissionValidationError(validationContext, node, fieldType.Name); - } + tasks.Add(_authorizationService.AuthorizeAsync(userContext.User, permission.Permission, permission.Resource)); } - else - { - var tasks = new List>(); - foreach (var permission in permissions) - { - tasks.Add(_authorizationService.AuthorizeAsync(userContext.User, permission.Permission, permission.Resource)); - } - - var authorizationResults = await Task.WhenAll(tasks); + var authorizationResults = await Task.WhenAll(tasks); - if (authorizationResults.Any(isAuthorized => !isAuthorized)) - { - AddPermissionValidationError(validationContext, node, fieldType.Name); - } + if (authorizationResults.Any(isAuthorized => !isAuthorized)) + { + AddPermissionValidationError(validationContext, node, fieldType.Name); } } + } - private void AddPermissionValidationError(ValidationContext validationContext, ASTNode node, string nodeName) - { - validationContext.ReportError(new ValidationError( - validationContext.Document.Source, - ErrorCode, - S["Authorization is required to access the node. {0}", nodeName], - node)); - } + private void AddPermissionValidationError(ValidationContext validationContext, ASTNode node, string nodeName) + { + validationContext.ReportError(new ValidationError( + validationContext.Document.Source, + ErrorCode, + S["Authorization is required to access the node. {0}", nodeName], + node)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Controllers/AdminController.cs index 257ea9ed920..bc8e3336a61 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Controllers/AdminController.cs @@ -16,147 +16,146 @@ using OrchardCore.Routing; using YesSql.Filters.Query; -namespace OrchardCore.AuditTrail.Controllers +namespace OrchardCore.AuditTrail.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly PagerOptions _pagerOptions; + private readonly IShapeFactory _shapeFactory; + private readonly IAuditTrailManager _auditTrailManager; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IAuditTrailAdminListQueryService _auditTrailAdminListQueryService; + private readonly IDisplayManager _displayManager; + private readonly IDisplayManager _auditTrailOptionsDisplayManager; + protected readonly IStringLocalizer S; + + public AdminController( + IOptions pagerOptions, + IShapeFactory shapeFactory, + IAuditTrailManager auditTrailManager, + IUpdateModelAccessor updateModelAccessor, + IAuthorizationService authorizationService, + IAuditTrailAdminListQueryService auditTrailAdminListQueryService, + IDisplayManager displayManager, + IDisplayManager auditTrailOptionsDisplayManager, + IStringLocalizer stringLocalizer) + { + _pagerOptions = pagerOptions.Value; + _shapeFactory = shapeFactory; + _auditTrailManager = auditTrailManager; + _updateModelAccessor = updateModelAccessor; + _authorizationService = authorizationService; + _auditTrailAdminListQueryService = auditTrailAdminListQueryService; + _displayManager = displayManager; + _auditTrailOptionsDisplayManager = auditTrailOptionsDisplayManager; + S = stringLocalizer; + } + + [Admin("AuditTrail/{correlationId?}", "AuditTrailIndex")] + public async Task Index([ModelBinder(BinderType = typeof(AuditTrailFilterEngineModelBinder), Name = "q")] QueryFilterResult queryFilterResult, PagerParameters pagerParameters, string correlationId = "") { - private readonly PagerOptions _pagerOptions; - private readonly IShapeFactory _shapeFactory; - private readonly IAuditTrailManager _auditTrailManager; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IAuditTrailAdminListQueryService _auditTrailAdminListQueryService; - private readonly IDisplayManager _displayManager; - private readonly IDisplayManager _auditTrailOptionsDisplayManager; - protected readonly IStringLocalizer S; - - public AdminController( - IOptions pagerOptions, - IShapeFactory shapeFactory, - IAuditTrailManager auditTrailManager, - IUpdateModelAccessor updateModelAccessor, - IAuthorizationService authorizationService, - IAuditTrailAdminListQueryService auditTrailAdminListQueryService, - IDisplayManager displayManager, - IDisplayManager auditTrailOptionsDisplayManager, - IStringLocalizer stringLocalizer) + if (!await _authorizationService.AuthorizeAsync(User, AuditTrailPermissions.ViewAuditTrail)) { - _pagerOptions = pagerOptions.Value; - _shapeFactory = shapeFactory; - _auditTrailManager = auditTrailManager; - _updateModelAccessor = updateModelAccessor; - _authorizationService = authorizationService; - _auditTrailAdminListQueryService = auditTrailAdminListQueryService; - _displayManager = displayManager; - _auditTrailOptionsDisplayManager = auditTrailOptionsDisplayManager; - S = stringLocalizer; + return Forbid(); } - [Admin("AuditTrail/{correlationId?}", "AuditTrailIndex")] - public async Task Index([ModelBinder(BinderType = typeof(AuditTrailFilterEngineModelBinder), Name = "q")] QueryFilterResult queryFilterResult, PagerParameters pagerParameters, string correlationId = "") + var options = new AuditTrailIndexOptions { - if (!await _authorizationService.AuthorizeAsync(User, AuditTrailPermissions.ViewAuditTrail)) - { - return Forbid(); - } - - var options = new AuditTrailIndexOptions - { - FilterResult = queryFilterResult - }; - - // This is used by Contents feature for routing so needs to be passed into the options. - if (!string.IsNullOrEmpty(correlationId)) - { - options.CorrelationId = correlationId; - options.CorrelationIdFromRoute = true; - } - - if (options.CorrelationIdFromRoute) - { - // When the correlation id is provided via the route or options a placeholder node is used to apply a filter. - options.FilterResult.TryAddOrReplace(new CorrelationIdFilterNode(options.CorrelationId)); - } - - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - - // With the options populated we filter the query, allowing the filters to alter the options. - var result = await _auditTrailAdminListQueryService.QueryAsync(pager.Page, pager.PageSize, options); - - // The search text is provided back to the UI. - options.SearchText = options.FilterResult.ToString(); - options.OriginalSearchText = options.SearchText; - - // Populate route values to maintain previous route data when generating page links. - options.RouteValues.TryAdd("q", options.FilterResult.ToString()); - - var pagerShape = await _shapeFactory.PagerAsync(pager, result.TotalCount, options.RouteValues); - var items = new List(); - - foreach (var auditTrailEvent in result.Events) - { - items.Add( - await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "SummaryAdmin") - ); - } - - var startIndex = (pager.Page - 1) * pager.PageSize + 1; - options.StartIndex = startIndex; - options.EndIndex = startIndex + items.Count - 1; - options.EventsCount = items.Count; - options.TotalItemCount = result.TotalCount; - - var header = await _auditTrailOptionsDisplayManager.BuildEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); - - var shapeViewModel = await _shapeFactory.CreateAsync("AuditTrailAdminList", viewModel => - { - viewModel.Events = items; - viewModel.Pager = pagerShape; - viewModel.Options = options; - viewModel.Header = header; - }); - - return View(shapeViewModel); + FilterResult = queryFilterResult + }; + + // This is used by Contents feature for routing so needs to be passed into the options. + if (!string.IsNullOrEmpty(correlationId)) + { + options.CorrelationId = correlationId; + options.CorrelationIdFromRoute = true; } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public async Task IndexFilterPOST(AuditTrailIndexOptions options) + if (options.CorrelationIdFromRoute) { - await _auditTrailOptionsDisplayManager.UpdateEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); - // When the user has typed something into the search input no further evaluation of the form post is required. - if (!string.Equals(options.SearchText, options.OriginalSearchText, StringComparison.OrdinalIgnoreCase)) - { - return RedirectToAction(nameof(Index), new RouteValueDictionary { { "q", options.SearchText } }); - } + // When the correlation id is provided via the route or options a placeholder node is used to apply a filter. + options.FilterResult.TryAddOrReplace(new CorrelationIdFilterNode(options.CorrelationId)); + } - // Evaluate the values provided in the form post and map them to the filter result and route values. - await _auditTrailOptionsDisplayManager.UpdateEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - // The route value must always be added after the editors have updated the models. - options.RouteValues.TryAdd("q", options.FilterResult.ToString()); + // With the options populated we filter the query, allowing the filters to alter the options. + var result = await _auditTrailAdminListQueryService.QueryAsync(pager.Page, pager.PageSize, options); - return RedirectToAction(nameof(Index), options.RouteValues); + // The search text is provided back to the UI. + options.SearchText = options.FilterResult.ToString(); + options.OriginalSearchText = options.SearchText; + + // Populate route values to maintain previous route data when generating page links. + options.RouteValues.TryAdd("q", options.FilterResult.ToString()); + + var pagerShape = await _shapeFactory.PagerAsync(pager, result.TotalCount, options.RouteValues); + var items = new List(); + + foreach (var auditTrailEvent in result.Events) + { + items.Add( + await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "SummaryAdmin") + ); } - [Admin("AuditTrail/Display/{auditTrailEventId}", "AuditTrailDisplay")] - public async Task Display(string auditTrailEventId) + var startIndex = (pager.Page - 1) * pager.PageSize + 1; + options.StartIndex = startIndex; + options.EndIndex = startIndex + items.Count - 1; + options.EventsCount = items.Count; + options.TotalItemCount = result.TotalCount; + + var header = await _auditTrailOptionsDisplayManager.BuildEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); + + var shapeViewModel = await _shapeFactory.CreateAsync("AuditTrailAdminList", viewModel => + { + viewModel.Events = items; + viewModel.Pager = pagerShape; + viewModel.Options = options; + viewModel.Header = header; + }); + + return View(shapeViewModel); + } + + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public async Task IndexFilterPOST(AuditTrailIndexOptions options) + { + await _auditTrailOptionsDisplayManager.UpdateEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); + // When the user has typed something into the search input no further evaluation of the form post is required. + if (!string.Equals(options.SearchText, options.OriginalSearchText, StringComparison.OrdinalIgnoreCase)) { - if (!await _authorizationService.AuthorizeAsync(User, AuditTrailPermissions.ViewAuditTrail)) - { - return Forbid(); - } + return RedirectToAction(nameof(Index), new RouteValueDictionary { { "q", options.SearchText } }); + } + + // Evaluate the values provided in the form post and map them to the filter result and route values. + await _auditTrailOptionsDisplayManager.UpdateEditorAsync(options, _updateModelAccessor.ModelUpdater, false, string.Empty, string.Empty); - var auditTrailEvent = await _auditTrailManager.GetEventAsync(auditTrailEventId); - if (auditTrailEvent == null) - { - return NotFound(); - } + // The route value must always be added after the editors have updated the models. + options.RouteValues.TryAdd("q", options.FilterResult.ToString()); + return RedirectToAction(nameof(Index), options.RouteValues); + } - var shape = await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "DetailAdmin"); + [Admin("AuditTrail/Display/{auditTrailEventId}", "AuditTrailDisplay")] + public async Task Display(string auditTrailEventId) + { + if (!await _authorizationService.AuthorizeAsync(User, AuditTrailPermissions.ViewAuditTrail)) + { + return Forbid(); + } - return View(new AuditTrailItemViewModel { Shape = shape }); + var auditTrailEvent = await _auditTrailManager.GetEventAsync(auditTrailEventId); + if (auditTrailEvent == null) + { + return NotFound(); } + + + var shape = await _displayManager.BuildDisplayAsync(auditTrailEvent, updater: _updateModelAccessor.ModelUpdater, displayType: "DetailAdmin"); + + return View(new AuditTrailItemViewModel { Shape = shape }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailEventDisplayDriver.cs index 6243e2a01de..37f5002015c 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailEventDisplayDriver.cs @@ -6,37 +6,36 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AuditTrail.Drivers +namespace OrchardCore.AuditTrail.Drivers; + +public sealed class AuditTrailEventDisplayDriver : DisplayDriver { - public sealed class AuditTrailEventDisplayDriver : DisplayDriver - { - private readonly IAuditTrailManager _auditTrailManager; + private readonly IAuditTrailManager _auditTrailManager; - public AuditTrailEventDisplayDriver(IAuditTrailManager auditTrailManager) - { - _auditTrailManager = auditTrailManager; - } + public AuditTrailEventDisplayDriver(IAuditTrailManager auditTrailManager) + { + _auditTrailManager = auditTrailManager; + } - public override Task DisplayAsync(AuditTrailEvent auditTrailEvent, BuildDisplayContext context) - { - var descriptor = _auditTrailManager.DescribeEvent(auditTrailEvent); + public override Task DisplayAsync(AuditTrailEvent auditTrailEvent, BuildDisplayContext context) + { + var descriptor = _auditTrailManager.DescribeEvent(auditTrailEvent); - return CombineAsync( - Initialize("AuditTrailEventTags_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) - .Location("SummaryAdmin", "EventTags:10"), - Initialize("AuditTrailEventMeta_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) - .Location("SummaryAdmin", "EventMeta:10"), - Initialize("AuditTrailEventActions_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) - .Location("SummaryAdmin", "Actions:10"), - Initialize("AuditTrailEventDetail_DetailAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) - .Location("DetailAdmin", "Content:before") - ); - } + return CombineAsync( + Initialize("AuditTrailEventTags_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) + .Location("SummaryAdmin", "EventTags:10"), + Initialize("AuditTrailEventMeta_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) + .Location("SummaryAdmin", "EventMeta:10"), + Initialize("AuditTrailEventActions_SummaryAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) + .Location("SummaryAdmin", "Actions:10"), + Initialize("AuditTrailEventDetail_DetailAdmin", model => BuildViewModel(auditTrailEvent, model, descriptor)) + .Location("DetailAdmin", "Content:before") + ); + } - public static void BuildViewModel(AuditTrailEvent auditTrailEvent, AuditTrailEventViewModel model, AuditTrailEventDescriptor descriptor) - { - model.AuditTrailEvent = auditTrailEvent; - model.Descriptor = descriptor; - } + public static void BuildViewModel(AuditTrailEvent auditTrailEvent, AuditTrailEventViewModel model, AuditTrailEventDescriptor descriptor) + { + model.AuditTrailEvent = auditTrailEvent; + model.Descriptor = descriptor; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailOptionsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailOptionsDisplayDriver.cs index ff58038d201..a7474365b2c 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailOptionsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailOptionsDisplayDriver.cs @@ -3,67 +3,66 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.AuditTrail.Drivers +namespace OrchardCore.AuditTrail.Drivers; + +public sealed class AuditTrailOptionsDisplayDriver : DisplayDriver { - public sealed class AuditTrailOptionsDisplayDriver : DisplayDriver + // Maintain the Options prefix for compatibility with binding. + protected override void BuildPrefix(AuditTrailIndexOptions options, string htmlFieldPrefix) { - // Maintain the Options prefix for compatibility with binding. - protected override void BuildPrefix(AuditTrailIndexOptions options, string htmlFieldPrefix) - { - Prefix = "Options"; - } + Prefix = "Options"; + } - public override Task DisplayAsync(AuditTrailIndexOptions model, BuildDisplayContext context) - { - return CombineAsync( - View("AuditTrailAdminFilters_Thumbnail__Category", model).Location("Thumbnail", "Content:10"), - View("AuditTrailAdminFilters_Thumbnail__Event", model).Location("Thumbnail", "Content:10"), - View("AuditTrailAdminFilters_Thumbnail__Date", model).Location("Thumbnail", "Content:20"), - View("AuditTrailAdminFilters_Thumbnail__UserName", model).Location("Thumbnail", "Content:30"), - View("AuditTrailAdminFilters_Thumbnail__UserId", model).Location("Thumbnail", "Content:40"), - View("AuditTrailAdminFilters_Thumbnail__CorrelationId", model).Location("Thumbnail", "Content:50") - ); - } + public override Task DisplayAsync(AuditTrailIndexOptions model, BuildDisplayContext context) + { + return CombineAsync( + View("AuditTrailAdminFilters_Thumbnail__Category", model).Location("Thumbnail", "Content:10"), + View("AuditTrailAdminFilters_Thumbnail__Event", model).Location("Thumbnail", "Content:10"), + View("AuditTrailAdminFilters_Thumbnail__Date", model).Location("Thumbnail", "Content:20"), + View("AuditTrailAdminFilters_Thumbnail__UserName", model).Location("Thumbnail", "Content:30"), + View("AuditTrailAdminFilters_Thumbnail__UserId", model).Location("Thumbnail", "Content:40"), + View("AuditTrailAdminFilters_Thumbnail__CorrelationId", model).Location("Thumbnail", "Content:50") + ); + } - public override Task EditAsync(AuditTrailIndexOptions model, BuildEditorContext context) - { - // Map the filter result to a model so the ui can reflect current selections. - model.FilterResult.MapTo(model); + public override Task EditAsync(AuditTrailIndexOptions model, BuildEditorContext context) + { + // Map the filter result to a model so the ui can reflect current selections. + model.FilterResult.MapTo(model); - return CombineAsync( - Initialize("AuditTrailAdminListSearch", m => BuildUserOptionsViewModel(m, model)).Location("Search:10"), - Initialize("AuditTrailAdminListSummary", m => BuildUserOptionsViewModel(m, model)).Location("Summary:10"), - Initialize("AuditTrailAdminListFilters", m => BuildUserOptionsViewModel(m, model)).Location("Actions:10.1") - ); - } + return CombineAsync( + Initialize("AuditTrailAdminListSearch", m => BuildUserOptionsViewModel(m, model)).Location("Search:10"), + Initialize("AuditTrailAdminListSummary", m => BuildUserOptionsViewModel(m, model)).Location("Summary:10"), + Initialize("AuditTrailAdminListFilters", m => BuildUserOptionsViewModel(m, model)).Location("Actions:10.1") + ); + } - public override Task UpdateAsync(AuditTrailIndexOptions model, UpdateEditorContext context) - { - // Map the incoming values from a form post to the filter result. - model.FilterResult.MapFrom(model); + public override Task UpdateAsync(AuditTrailIndexOptions model, UpdateEditorContext context) + { + // Map the incoming values from a form post to the filter result. + model.FilterResult.MapFrom(model); - return EditAsync(model, context); - } + return EditAsync(model, context); + } - private static void BuildUserOptionsViewModel(AuditTrailIndexOptions m, AuditTrailIndexOptions model) - { - m.SearchText = model.SearchText; - m.OriginalSearchText = model.OriginalSearchText; - m.Category = model.Category; - m.CorrelationId = model.CorrelationId; - m.CorrelationIdFromRoute = model.CorrelationIdFromRoute; - m.Categories = model.Categories; - m.Event = model.Event; - m.Events = model.Events; - m.Sort = model.Sort; - m.AuditTrailSorts = model.AuditTrailSorts; - m.Date = model.Date; - m.AuditTrailDates = model.AuditTrailDates; - m.StartIndex = model.StartIndex; - m.EndIndex = model.EndIndex; - m.EventsCount = model.EventsCount; - m.TotalItemCount = model.TotalItemCount; - m.FilterResult = model.FilterResult; - } + private static void BuildUserOptionsViewModel(AuditTrailIndexOptions m, AuditTrailIndexOptions model) + { + m.SearchText = model.SearchText; + m.OriginalSearchText = model.OriginalSearchText; + m.Category = model.Category; + m.CorrelationId = model.CorrelationId; + m.CorrelationIdFromRoute = model.CorrelationIdFromRoute; + m.Categories = model.Categories; + m.Event = model.Event; + m.Events = model.Events; + m.Sort = model.Sort; + m.AuditTrailSorts = model.AuditTrailSorts; + m.Date = model.Date; + m.AuditTrailDates = model.AuditTrailDates; + m.StartIndex = model.StartIndex; + m.EndIndex = model.EndIndex; + m.EventsCount = model.EventsCount; + m.TotalItemCount = model.TotalItemCount; + m.FilterResult = model.FilterResult; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailSettingsDisplayDriver.cs index 10d1e871ca2..b36e79c7c42 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailSettingsDisplayDriver.cs @@ -11,108 +11,107 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.AuditTrail.Drivers +namespace OrchardCore.AuditTrail.Drivers; + +public sealed class AuditTrailSettingsDisplayDriver : SiteDisplayDriver { - public sealed class AuditTrailSettingsDisplayDriver : SiteDisplayDriver + private readonly IAuditTrailManager _auditTrailManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IServiceProvider _serviceProvider; + + public AuditTrailSettingsDisplayDriver( + IAuditTrailManager auditTrailManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IServiceProvider serviceProvider) { - private readonly IAuditTrailManager _auditTrailManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IServiceProvider _serviceProvider; + _auditTrailManager = auditTrailManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _serviceProvider = serviceProvider; + } - public AuditTrailSettingsDisplayDriver( - IAuditTrailManager auditTrailManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IServiceProvider serviceProvider) + protected override string SettingsGroupId + => AuditTrailSettingsGroup.Id; + + public override async Task EditAsync(ISite site, AuditTrailSettings settings, BuildEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ManageAuditTrailSettings)) { - _auditTrailManager = auditTrailManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _serviceProvider = serviceProvider; + return null; } - protected override string SettingsGroupId - => AuditTrailSettingsGroup.Id; - - public override async Task EditAsync(ISite site, AuditTrailSettings settings, BuildEditorContext context) + return Initialize("AuditTrailSettings_Edit", model => { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } + var categories = _auditTrailManager.DescribeCategories(); - return Initialize("AuditTrailSettings_Edit", model => - { - var categories = _auditTrailManager.DescribeCategories(); + var settingsGroups = settings.Categories + .ToLookup(category => category.Events + .FirstOrDefault()?.Category ?? ""); - var settingsGroups = settings.Categories - .ToLookup(category => category.Events - .FirstOrDefault()?.Category ?? ""); + var categoriesViewModel = categories + .Select(category => new AuditTrailCategorySettingsViewModel() + { + Name = category.Name, + LocalizedName = category.LocalizedName(_serviceProvider), + Events = category.Events.Values + .Select(auditTrailEvent => + { + var settings = settingsGroups[auditTrailEvent.Category] + .FirstOrDefault()?.Events + .FirstOrDefault(settings => settings.Name == auditTrailEvent.Name); - var categoriesViewModel = categories - .Select(category => new AuditTrailCategorySettingsViewModel() - { - Name = category.Name, - LocalizedName = category.LocalizedName(_serviceProvider), - Events = category.Events.Values - .Select(auditTrailEvent => + return new AuditTrailEventSettingsViewModel() { - var settings = settingsGroups[auditTrailEvent.Category] - .FirstOrDefault()?.Events - .FirstOrDefault(settings => settings.Name == auditTrailEvent.Name); + Name = auditTrailEvent.Name, + Category = auditTrailEvent.Category, + LocalizedName = auditTrailEvent.LocalizedName(_serviceProvider), + Description = auditTrailEvent.Description(_serviceProvider), + IsEnabled = auditTrailEvent.IsMandatory || (settings?.IsEnabled ?? auditTrailEvent.IsEnabledByDefault), + IsMandatory = auditTrailEvent.IsMandatory + }; + }) + .ToArray() + }) + .ToArray(); - return new AuditTrailEventSettingsViewModel() - { - Name = auditTrailEvent.Name, - Category = auditTrailEvent.Category, - LocalizedName = auditTrailEvent.LocalizedName(_serviceProvider), - Description = auditTrailEvent.Description(_serviceProvider), - IsEnabled = auditTrailEvent.IsMandatory || (settings?.IsEnabled ?? auditTrailEvent.IsEnabledByDefault), - IsMandatory = auditTrailEvent.IsMandatory - }; - }) - .ToArray() - }) - .ToArray(); + model.Categories = categoriesViewModel; + model.ClientIpAddressAllowed = settings.ClientIpAddressAllowed; + }).Location("Content:1#Events") + .OnGroup(SettingsGroupId); + } - model.Categories = categoriesViewModel; - model.ClientIpAddressAllowed = settings.ClientIpAddressAllowed; - }).Location("Content:1#Events") - .OnGroup(SettingsGroupId); + public override async Task UpdateAsync(ISite site, AuditTrailSettings settings, UpdateEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ManageAuditTrailSettings)) + { + return null; } - public override async Task UpdateAsync(ISite site, AuditTrailSettings settings, UpdateEditorContext context) + if (context.GroupId == AuditTrailSettingsGroup.Id) { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } - - if (context.GroupId == AuditTrailSettingsGroup.Id) - { - var model = new AuditTrailSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - settings.Categories = model.Categories - .Select(categorySettings => new AuditTrailCategorySettings() - { - Name = categorySettings.Name, - Events = categorySettings.Events - .Select(settings => new AuditTrailEventSettings() - { - Name = settings.Name, - Category = settings.Category, - IsEnabled = settings.IsEnabled - }) - .ToArray() - }) - .ToArray(); + var model = new AuditTrailSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.ClientIpAddressAllowed = model.ClientIpAddressAllowed; - } + settings.Categories = model.Categories + .Select(categorySettings => new AuditTrailCategorySettings() + { + Name = categorySettings.Name, + Events = categorySettings.Events + .Select(settings => new AuditTrailEventSettings() + { + Name = settings.Name, + Category = settings.Category, + IsEnabled = settings.IsEnabled + }) + .ToArray() + }) + .ToArray(); - return await EditAsync(site, settings, context); + settings.ClientIpAddressAllowed = model.ClientIpAddressAllowed; } + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailTrimmingSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailTrimmingSettingsDisplayDriver.cs index 383dda9d18f..9aba9a6f5a1 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailTrimmingSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Drivers/AuditTrailTrimmingSettingsDisplayDriver.cs @@ -8,53 +8,52 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.AuditTrail.Drivers +namespace OrchardCore.AuditTrail.Drivers; + +public sealed class AuditTrailTrimmingSettingsDisplayDriver : SiteDisplayDriver { - public sealed class AuditTrailTrimmingSettingsDisplayDriver : SiteDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public AuditTrailTrimmingSettingsDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public AuditTrailTrimmingSettingsDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService) + protected override string SettingsGroupId + => AuditTrailSettingsGroup.Id; + + public override async Task EditAsync(ISite site, AuditTrailTrimmingSettings section, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => AuditTrailSettingsGroup.Id; - - public override async Task EditAsync(ISite site, AuditTrailTrimmingSettings section, BuildEditorContext context) + return Initialize("AuditTrailTrimmingSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } - - return Initialize("AuditTrailTrimmingSettings_Edit", model => - { - model.RetentionDays = section.RetentionDays; - model.LastRunUtc = section.LastRunUtc; - model.Disabled = section.Disabled; - }).Location("Content:10#Trimming;0") - .OnGroup(SettingsGroupId); - } + model.RetentionDays = section.RetentionDays; + model.LastRunUtc = section.LastRunUtc; + model.Disabled = section.Disabled; + }).Location("Content:10#Trimming;0") + .OnGroup(SettingsGroupId); + } - public override async Task UpdateAsync(ISite site, AuditTrailTrimmingSettings section, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, AuditTrailTrimmingSettings section, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } - - var model = new AuditTrailTrimmingSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - section.RetentionDays = model.RetentionDays; - section.Disabled = model.Disabled; - - return await EditAsync(site, section, context); + return null; } + + var model = new AuditTrailTrimmingSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + section.RetentionDays = model.RetentionDays; + section.Disabled = model.Disabled; + + return await EditAsync(site, section, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Indexes/AuditTrailEventIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Indexes/AuditTrailEventIndexProvider.cs index 2234cdbfa2f..841c60f8b7f 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Indexes/AuditTrailEventIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Indexes/AuditTrailEventIndexProvider.cs @@ -1,26 +1,25 @@ using OrchardCore.AuditTrail.Models; using YesSql.Indexes; -namespace OrchardCore.AuditTrail.Indexes +namespace OrchardCore.AuditTrail.Indexes; + +public class AuditTrailEventIndexProvider : IndexProvider { - public class AuditTrailEventIndexProvider : IndexProvider - { - public AuditTrailEventIndexProvider() => CollectionName = AuditTrailEvent.Collection; + public AuditTrailEventIndexProvider() => CollectionName = AuditTrailEvent.Collection; - public override void Describe(DescribeContext context) => - context.For() - .Map(auditTrailEvent => + public override void Describe(DescribeContext context) => + context.For() + .Map(auditTrailEvent => + { + return new AuditTrailEventIndex { - return new AuditTrailEventIndex - { - EventId = auditTrailEvent.EventId, - Category = auditTrailEvent.Category, - Name = auditTrailEvent.Name, - CorrelationId = auditTrailEvent.CorrelationId, - UserId = auditTrailEvent.UserId, - NormalizedUserName = auditTrailEvent.NormalizedUserName, - CreatedUtc = auditTrailEvent.CreatedUtc - }; - }); - } + EventId = auditTrailEvent.EventId, + Category = auditTrailEvent.Category, + Name = auditTrailEvent.Name, + CorrelationId = auditTrailEvent.CorrelationId, + UserId = auditTrailEvent.UserId, + NormalizedUserName = auditTrailEvent.NormalizedUserName, + CreatedUtc = auditTrailEvent.CreatedUtc + }; + }); } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Migrations.cs index c82bfc3419f..152ada1109d 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Migrations.cs @@ -5,37 +5,36 @@ using OrchardCore.Data.Migration; using YesSql.Sql; -namespace OrchardCore.AuditTrail +namespace OrchardCore.AuditTrail; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + public async Task CreateAsync() { - public async Task CreateAsync() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column(nameof(AuditTrailEventIndex.EventId), column => column.WithLength(26)) - .Column(nameof(AuditTrailEventIndex.Category), column => column.WithLength(64)) - .Column(nameof(AuditTrailEventIndex.Name), column => column.WithLength(64)) - .Column(nameof(AuditTrailEventIndex.CorrelationId), column => column.WithLength(26)) - .Column(nameof(AuditTrailEventIndex.UserId), column => column.WithLength(26)) - .Column(nameof(AuditTrailEventIndex.NormalizedUserName), column => column.Nullable().WithLength(255)) - .Column(nameof(AuditTrailEventIndex.CreatedUtc), column => column.Nullable()), - collection: AuditTrailEvent.Collection); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column(nameof(AuditTrailEventIndex.EventId), column => column.WithLength(26)) + .Column(nameof(AuditTrailEventIndex.Category), column => column.WithLength(64)) + .Column(nameof(AuditTrailEventIndex.Name), column => column.WithLength(64)) + .Column(nameof(AuditTrailEventIndex.CorrelationId), column => column.WithLength(26)) + .Column(nameof(AuditTrailEventIndex.UserId), column => column.WithLength(26)) + .Column(nameof(AuditTrailEventIndex.NormalizedUserName), column => column.Nullable().WithLength(255)) + .Column(nameof(AuditTrailEventIndex.CreatedUtc), column => column.Nullable()), + collection: AuditTrailEvent.Collection); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_AuditTrailEventIndex_DocumentId", - "DocumentId", - "EventId", - "Category", - "Name", - "CorrelationId", - "UserId", - "NormalizedUserName", - "CreatedUtc" - ), - collection: AuditTrailEvent.Collection - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_AuditTrailEventIndex_DocumentId", + "DocumentId", + "EventId", + "Category", + "Name", + "CorrelationId", + "UserId", + "NormalizedUserName", + "CreatedUtc" + ), + collection: AuditTrailEvent.Collection + ); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailAdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailAdminMenu.cs index ee7b52f4b40..10278fb70fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailAdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailAdminMenu.cs @@ -4,40 +4,39 @@ using OrchardCore.AuditTrail.Controllers; using OrchardCore.Navigation; -namespace OrchardCore.AuditTrail.Navigation +namespace OrchardCore.AuditTrail.Navigation; + +public sealed class AuditTrailAdminMenu : INavigationProvider { - public sealed class AuditTrailAdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.AuditTrail" }, - { "correlationId", string.Empty }, - }; + { "area", "OrchardCore.AuditTrail" }, + { "correlationId", string.Empty }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AuditTrailAdminMenu(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public AuditTrailAdminMenu(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Audit Trail"], NavigationConstants.AdminMenuAuditTrailPosition, configuration => configuration - .AddClass("audittrail") - .Id("audittrail") - .Action(nameof(AdminController.Index), "Admin", _routeValues) - .Permission(AuditTrailPermissions.ViewAuditTrail) - .LocalNav() - ); - return Task.CompletedTask; } + + builder + .Add(S["Audit Trail"], NavigationConstants.AdminMenuAuditTrailPosition, configuration => configuration + .AddClass("audittrail") + .Id("audittrail") + .Action(nameof(AdminController.Index), "Admin", _routeValues) + .Permission(AuditTrailPermissions.ViewAuditTrail) + .LocalNav() + ); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailSettingsAdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailSettingsAdminMenu.cs index b83b86a79fc..d7f215049b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailSettingsAdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Navigation/AuditTrailSettingsAdminMenu.cs @@ -4,44 +4,43 @@ using OrchardCore.AuditTrail.Settings; using OrchardCore.Navigation; -namespace OrchardCore.AuditTrail.Navigation +namespace OrchardCore.AuditTrail.Navigation; + +public sealed class AuditTrailSettingsAdminMenu : INavigationProvider { - public sealed class AuditTrailSettingsAdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", AuditTrailSettingsGroup.Id }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", AuditTrailSettingsGroup.Id }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AuditTrailSettingsAdminMenu(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public AuditTrailSettingsAdminMenu(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Audit Trail"], S["Audit Trail"].PrefixPosition(), auditTrail => auditTrail - .AddClass("audittrail") - .Id("audittrailSettings") - .Action("Index", "Admin", _routeValues) - .Permission(AuditTrailPermissions.ManageAuditTrailSettings) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Audit Trail"], S["Audit Trail"].PrefixPosition(), auditTrail => auditTrail + .AddClass("audittrail") + .Id("audittrailSettings") + .Action("Index", "Admin", _routeValues) + .Permission(AuditTrailPermissions.ManageAuditTrailSettings) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailBackgroundTask.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailBackgroundTask.cs index d26bd79bea6..7add51b22d7 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailBackgroundTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailBackgroundTask.cs @@ -9,55 +9,54 @@ using OrchardCore.Modules; using OrchardCore.Settings; -namespace OrchardCore.AuditTrail.Services -{ - [BackgroundTask( - Title = "Audit Trail Events Purger", - Schedule = "0 0 * * *", - Description = "Regularly purges old Audit Trail events.", - LockTimeout = 3_000, LockExpiration = 30_000)] +namespace OrchardCore.AuditTrail.Services; + +[BackgroundTask( + Title = "Audit Trail Events Purger", + Schedule = "0 0 * * *", + Description = "Regularly purges old Audit Trail events.", + LockTimeout = 3_000, LockExpiration = 30_000)] - public sealed class AuditTrailBackgroundTask : IBackgroundTask +public sealed class AuditTrailBackgroundTask : IBackgroundTask +{ + public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) { - public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + var siteService = serviceProvider.GetRequiredService(); + + var settings = await siteService.GetSettingsAsync(); + if (settings.Disabled) { - var siteService = serviceProvider.GetRequiredService(); + return; + } - var settings = await siteService.GetSettingsAsync(); - if (settings.Disabled) - { - return; - } + var logger = serviceProvider.GetRequiredService>(); + logger.LogDebug("Audit Trail trimming: beginning sweep."); - var logger = serviceProvider.GetRequiredService>(); - logger.LogDebug("Audit Trail trimming: beginning sweep."); + try + { + var clock = serviceProvider.GetRequiredService(); + var auditTrailManager = serviceProvider.GetRequiredService(); - try - { - var clock = serviceProvider.GetRequiredService(); - var auditTrailManager = serviceProvider.GetRequiredService(); + logger.LogDebug("Starting Audit Trail trimming."); + var deletedEvents = await auditTrailManager.TrimEventsAsync(TimeSpan.FromDays(settings.RetentionDays)); + logger.LogDebug("Audit Trail trimming completed. {EventCount} events were deleted.", deletedEvents); + settings.LastRunUtc = clock.UtcNow; - logger.LogDebug("Starting Audit Trail trimming."); - var deletedEvents = await auditTrailManager.TrimEventsAsync(TimeSpan.FromDays(settings.RetentionDays)); - logger.LogDebug("Audit Trail trimming completed. {EventCount} events were deleted.", deletedEvents); + var container = await siteService.LoadSiteSettingsAsync(); + container.Alter(nameof(AuditTrailTrimmingSettings), settings => + { settings.LastRunUtc = clock.UtcNow; + }); - var container = await siteService.LoadSiteSettingsAsync(); - container.Alter(nameof(AuditTrailTrimmingSettings), settings => - { - settings.LastRunUtc = clock.UtcNow; - }); - - await siteService.UpdateSiteSettingsAsync(container); - } - catch (Exception ex) when (!ex.IsFatal()) - { - logger.LogError(ex, "Audit Trail trimming: error during sweep."); - } - finally - { - logger.LogDebug("Audit Trail trimming: ending sweep."); - } + await siteService.UpdateSiteSettingsAsync(container); + } + catch (Exception ex) when (!ex.IsFatal()) + { + logger.LogError(ex, "Audit Trail trimming: error during sweep."); + } + finally + { + logger.LogDebug("Audit Trail trimming: ending sweep."); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailIdGenerator.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailIdGenerator.cs index a56ad9cc518..c82b536b2b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailIdGenerator.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailIdGenerator.cs @@ -1,16 +1,15 @@ using OrchardCore.Entities; -namespace OrchardCore.AuditTrail.Services -{ - public class AuditTrailIdGenerator : IAuditTrailIdGenerator - { - private readonly IIdGenerator _generator; +namespace OrchardCore.AuditTrail.Services; - public AuditTrailIdGenerator(IIdGenerator generator) - { - _generator = generator; - } +public class AuditTrailIdGenerator : IAuditTrailIdGenerator +{ + private readonly IIdGenerator _generator; - public string GenerateUniqueId() => _generator.GenerateUniqueId(); + public AuditTrailIdGenerator(IIdGenerator generator) + { + _generator = generator; } + + public string GenerateUniqueId() => _generator.GenerateUniqueId(); } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailManager.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailManager.cs index bc8e8b14369..c0b15fa5ffe 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/AuditTrailManager.cs @@ -18,173 +18,172 @@ using OrchardCore.Settings; using YesSql; -namespace OrchardCore.AuditTrail.Services +namespace OrchardCore.AuditTrail.Services; + +public class AuditTrailManager : IAuditTrailManager { - public class AuditTrailManager : IAuditTrailManager + private readonly IClock _clock; + private readonly YesSql.ISession _session; + private readonly ISiteService _siteService; + private readonly AuditTrailOptions _auditTrailOptions; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILookupNormalizer _keyNormalizer; + private readonly IAuditTrailIdGenerator _auditTrailIdGenerator; + private readonly IEnumerable _auditTrailEventHandlers; + private readonly IClientIPAddressAccessor _clientIPAddressAccessor; + private readonly ShellSettings _shellSettings; + private readonly ILogger _logger; + + public AuditTrailManager( + IClock clock, + YesSql.ISession session, + ISiteService siteService, + IOptions auditTrailOptions, + IHttpContextAccessor httpContextAccessor, + ILookupNormalizer keyNormalizer, + IEnumerable auditTrailEventHandlers, + IClientIPAddressAccessor clientIPAddressAccessor, + IAuditTrailIdGenerator auditTrailIdGenerator, + ShellSettings shellSettings, + ILogger logger) { - private readonly IClock _clock; - private readonly YesSql.ISession _session; - private readonly ISiteService _siteService; - private readonly AuditTrailOptions _auditTrailOptions; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILookupNormalizer _keyNormalizer; - private readonly IAuditTrailIdGenerator _auditTrailIdGenerator; - private readonly IEnumerable _auditTrailEventHandlers; - private readonly IClientIPAddressAccessor _clientIPAddressAccessor; - private readonly ShellSettings _shellSettings; - private readonly ILogger _logger; - - public AuditTrailManager( - IClock clock, - YesSql.ISession session, - ISiteService siteService, - IOptions auditTrailOptions, - IHttpContextAccessor httpContextAccessor, - ILookupNormalizer keyNormalizer, - IEnumerable auditTrailEventHandlers, - IClientIPAddressAccessor clientIPAddressAccessor, - IAuditTrailIdGenerator auditTrailIdGenerator, - ShellSettings shellSettings, - ILogger logger) - { - _clock = clock; - _session = session; - _siteService = siteService; - _auditTrailOptions = auditTrailOptions.Value; - _httpContextAccessor = httpContextAccessor; - _keyNormalizer = keyNormalizer; - _auditTrailEventHandlers = auditTrailEventHandlers; - _clientIPAddressAccessor = clientIPAddressAccessor; - _auditTrailIdGenerator = auditTrailIdGenerator; - _shellSettings = shellSettings; - _logger = logger; - } + _clock = clock; + _session = session; + _siteService = siteService; + _auditTrailOptions = auditTrailOptions.Value; + _httpContextAccessor = httpContextAccessor; + _keyNormalizer = keyNormalizer; + _auditTrailEventHandlers = auditTrailEventHandlers; + _clientIPAddressAccessor = clientIPAddressAccessor; + _auditTrailIdGenerator = auditTrailIdGenerator; + _shellSettings = shellSettings; + _logger = logger; + } - public async Task RecordEventAsync(AuditTrailContext context) where TEvent : class, new() + public async Task RecordEventAsync(AuditTrailContext context) where TEvent : class, new() + { + if (_shellSettings.IsInitializing() && string.IsNullOrEmpty(context.UserName)) { - if (_shellSettings.IsInitializing() && string.IsNullOrEmpty(context.UserName)) + var feature = _httpContextAccessor.HttpContext.Features.Get(); + if (feature != null && feature.Properties.TryGetValue(SetupConstants.AdminUsername, out var adminUsername)) { - var feature = _httpContextAccessor.HttpContext.Features.Get(); - if (feature != null && feature.Properties.TryGetValue(SetupConstants.AdminUsername, out var adminUsername)) - { - context.UserName = (string)adminUsername; - } + context.UserName = (string)adminUsername; } + } - var descriptor = DescribeEvent(context.Name, context.Category); - if (descriptor == null || !await IsEventEnabledAsync(descriptor)) - { - return; - } + var descriptor = DescribeEvent(context.Name, context.Category); + if (descriptor == null || !await IsEventEnabledAsync(descriptor)) + { + return; + } - var createContext = new AuditTrailCreateContext( - context.Name, - context.Category, - context.CorrelationId, - context.UserId, - context.UserName, - context.AuditTrailEventItem - ); + var createContext = new AuditTrailCreateContext( + context.Name, + context.Category, + context.CorrelationId, + context.UserId, + context.UserName, + context.AuditTrailEventItem + ); - await _auditTrailEventHandlers.InvokeAsync((handler, context) => handler.CreateAsync(context), createContext, _logger); + await _auditTrailEventHandlers.InvokeAsync((handler, context) => handler.CreateAsync(context), createContext, _logger); - var auditTrailEvent = new AuditTrailEvent - { - EventId = _auditTrailIdGenerator.GenerateUniqueId(), - Category = createContext.Category, - Name = createContext.Name, - CorrelationId = createContext.CorrelationId, - UserId = createContext.UserId, - UserName = createContext.UserName ?? "", - NormalizedUserName = string.IsNullOrEmpty(createContext.UserName) ? "" : _keyNormalizer.NormalizeName(createContext.UserName), - ClientIpAddress = string.IsNullOrEmpty(createContext.ClientIpAddress) - ? await GetClientIpAddressAsync() - : createContext.ClientIpAddress, - CreatedUtc = createContext.CreatedUtc ?? _clock.UtcNow - }; - - auditTrailEvent.Put(createContext.AuditTrailEventItem); - - await _auditTrailEventHandlers.InvokeAsync((handler, context, auditTrailEvent) => handler.AlterAsync(context, auditTrailEvent), createContext, auditTrailEvent, _logger); - - await _session.SaveAsync(auditTrailEvent, AuditTrailEvent.Collection); - } - public Task GetEventAsync(string eventId) => - _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => index.EventId == eventId) - .FirstOrDefaultAsync(); - - public async Task TrimEventsAsync(TimeSpan retentionPeriod) + var auditTrailEvent = new AuditTrailEvent { - var dateThreshold = _clock.UtcNow.AddDays(1) - retentionPeriod; + EventId = _auditTrailIdGenerator.GenerateUniqueId(), + Category = createContext.Category, + Name = createContext.Name, + CorrelationId = createContext.CorrelationId, + UserId = createContext.UserId, + UserName = createContext.UserName ?? "", + NormalizedUserName = string.IsNullOrEmpty(createContext.UserName) ? "" : _keyNormalizer.NormalizeName(createContext.UserName), + ClientIpAddress = string.IsNullOrEmpty(createContext.ClientIpAddress) + ? await GetClientIpAddressAsync() + : createContext.ClientIpAddress, + CreatedUtc = createContext.CreatedUtc ?? _clock.UtcNow + }; + + auditTrailEvent.Put(createContext.AuditTrailEventItem); + + await _auditTrailEventHandlers.InvokeAsync((handler, context, auditTrailEvent) => handler.AlterAsync(context, auditTrailEvent), createContext, auditTrailEvent, _logger); + + await _session.SaveAsync(auditTrailEvent, AuditTrailEvent.Collection); + } + public Task GetEventAsync(string eventId) => + _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => index.EventId == eventId) + .FirstOrDefaultAsync(); - var events = await _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => index.CreatedUtc <= dateThreshold) - .ListAsync(); + public async Task TrimEventsAsync(TimeSpan retentionPeriod) + { + var dateThreshold = _clock.UtcNow.AddDays(1) - retentionPeriod; - var deletedEvents = 0; - foreach (var auditTrailEvent in events) - { - _session.Delete(auditTrailEvent, collection: AuditTrailEvent.Collection); - deletedEvents++; - } + var events = await _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => index.CreatedUtc <= dateThreshold) + .ListAsync(); - return deletedEvents; + var deletedEvents = 0; + foreach (var auditTrailEvent in events) + { + _session.Delete(auditTrailEvent, collection: AuditTrailEvent.Collection); + deletedEvents++; } - public AuditTrailEventDescriptor DescribeEvent(AuditTrailEvent auditTrailEvent) => - DescribeEvent(auditTrailEvent.Name, auditTrailEvent.Category) - ?? AuditTrailEventDescriptor.Default(auditTrailEvent); + return deletedEvents; + } - public IEnumerable DescribeCategories() - => _auditTrailOptions.CategoryDescriptors.Values; + public AuditTrailEventDescriptor DescribeEvent(AuditTrailEvent auditTrailEvent) => + DescribeEvent(auditTrailEvent.Name, auditTrailEvent.Category) + ?? AuditTrailEventDescriptor.Default(auditTrailEvent); - public AuditTrailCategoryDescriptor DescribeCategory(string name) - { - if (_auditTrailOptions.CategoryDescriptors.TryGetValue(name, out var category)) - { - return category; - } + public IEnumerable DescribeCategories() + => _auditTrailOptions.CategoryDescriptors.Values; - return AuditTrailCategoryDescriptor.Default(name); + public AuditTrailCategoryDescriptor DescribeCategory(string name) + { + if (_auditTrailOptions.CategoryDescriptors.TryGetValue(name, out var category)) + { + return category; } - private AuditTrailEventDescriptor DescribeEvent(string name, string categoryName) - { - var category = DescribeCategory(categoryName); - category.Events.TryGetValue(name, out var descriptor); + return AuditTrailCategoryDescriptor.Default(name); + } - return descriptor; - } + private AuditTrailEventDescriptor DescribeEvent(string name, string categoryName) + { + var category = DescribeCategory(categoryName); + category.Events.TryGetValue(name, out var descriptor); - private async Task GetClientIpAddressAsync() - { - var settings = await GetAuditTrailSettingsAsync(); - if (!settings.ClientIpAddressAllowed) - { - return null; - } + return descriptor; + } - return (await _clientIPAddressAccessor.GetIPAddressAsync())?.ToString(); + private async Task GetClientIpAddressAsync() + { + var settings = await GetAuditTrailSettingsAsync(); + if (!settings.ClientIpAddressAllowed) + { + return null; } - private async Task GetAuditTrailSettingsAsync() => - (await _siteService.GetSiteSettingsAsync()).As(); + return (await _clientIPAddressAccessor.GetIPAddressAsync())?.ToString(); + } + + private async Task GetAuditTrailSettingsAsync() => + (await _siteService.GetSiteSettingsAsync()).As(); - private async Task IsEventEnabledAsync(AuditTrailEventDescriptor descriptor) + private async Task IsEventEnabledAsync(AuditTrailEventDescriptor descriptor) + { + if (descriptor.IsMandatory) { - if (descriptor.IsMandatory) - { - return true; - } + return true; + } - var settings = await GetAuditTrailSettingsAsync(); + var settings = await GetAuditTrailSettingsAsync(); - var eventSettings = settings.Categories - .FirstOrDefault(category => category.Name == descriptor.Category)?.Events - .FirstOrDefault(settings => settings.Name == descriptor.Name); + var eventSettings = settings.Categories + .FirstOrDefault(category => category.Name == descriptor.Category)?.Events + .FirstOrDefault(settings => settings.Name == descriptor.Name); - return eventSettings?.IsEnabled ?? descriptor.IsEnabledByDefault; - } + return eventSettings?.IsEnabled ?? descriptor.IsEnabledByDefault; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/CorrelationIdFilterNode.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/CorrelationIdFilterNode.cs index a53f685efad..3dc10b4e7ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/CorrelationIdFilterNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/CorrelationIdFilterNode.cs @@ -1,20 +1,19 @@ using YesSql.Filters.Abstractions.Nodes; -namespace OrchardCore.AuditTrail.Services +namespace OrchardCore.AuditTrail.Services; + +/// +/// Provides a correlation id node is used when a filter has not been selected. +/// +public class CorrelationIdFilterNode : TermOperationNode { - /// - /// Provides a correlation id node is used when a filter has not been selected. - /// - public class CorrelationIdFilterNode : TermOperationNode + public CorrelationIdFilterNode(string correlationId) : base("id", new UnaryNode(correlationId, OperateNodeQuotes.None)) { - public CorrelationIdFilterNode(string correlationId) : base("id", new UnaryNode(correlationId, OperateNodeQuotes.None)) - { - } + } - public override string ToNormalizedString() - => string.Empty; + public override string ToNormalizedString() + => string.Empty; - public override string ToString() - => string.Empty; - } + public override string ToString() + => string.Empty; } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DateTimeParser.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DateTimeParser.cs index 142c235194a..199530d5619 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DateTimeParser.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DateTimeParser.cs @@ -6,263 +6,262 @@ using Parlot.Fluent; using static Parlot.Fluent.Parsers; -namespace OrchardCore.AuditTrail.Services +namespace OrchardCore.AuditTrail.Services; + +public readonly struct BuildExpressionContext { - public readonly struct BuildExpressionContext + public BuildExpressionContext(DateTime utcNow, ParameterExpression parameter, MemberExpression member, Type type) { - public BuildExpressionContext(DateTime utcNow, ParameterExpression parameter, MemberExpression member, Type type) + UtcNow = utcNow; + Parameter = parameter; + Member = member; + Type = type; + } + + public DateTime UtcNow { get; } + public ParameterExpression Parameter { get; } + public MemberExpression Member { get; } + public Type Type { get; } +} + +public abstract class Node +{ + public abstract Expression BuildExpression(in BuildExpressionContext context); +} + +public abstract class OperatorNode : Node +{ + public string Operator { get; set; } + + public Expression BuildOperation(in BuildExpressionContext context, ConstantExpression constant) + { + if (string.IsNullOrEmpty(Operator)) { - UtcNow = utcNow; - Parameter = parameter; - Member = member; - Type = type; + return constant; } - public DateTime UtcNow { get; } - public ParameterExpression Parameter { get; } - public MemberExpression Member { get; } - public Type Type { get; } + return Operator switch + { + ">" => Expression.GreaterThan(context.Member, constant), + ">=" => Expression.GreaterThanOrEqual(context.Member, constant), + "<" => Expression.LessThan(context.Member, constant), + "<=" => Expression.LessThanOrEqual(context.Member, constant), + _ => null + }; } +} - public abstract class Node +public class DateNode : OperatorNode +{ + public DateNode(DateTimeOffset dateTime) { - public abstract Expression BuildExpression(in BuildExpressionContext context); + DateTime = dateTime; } - public abstract class OperatorNode : Node - { - public string Operator { get; set; } + public DateTimeOffset DateTime { get; } - public Expression BuildOperation(in BuildExpressionContext context, ConstantExpression constant) - { - if (string.IsNullOrEmpty(Operator)) - { - return constant; - } + public override Expression BuildExpression(in BuildExpressionContext context) + => BuildOperation(context, Expression.Constant(DateTime.UtcDateTime, typeof(DateTime))); - return Operator switch - { - ">" => Expression.GreaterThan(context.Member, constant), - ">=" => Expression.GreaterThanOrEqual(context.Member, constant), - "<" => Expression.LessThan(context.Member, constant), - "<=" => Expression.LessThanOrEqual(context.Member, constant), - _ => null - }; - } - } + public override string ToString() + => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}{DateTime:o}"; +} - public class DateNode : OperatorNode +public class DateNode2 : OperatorNode +{ + public DateNode2(DateTime dateTime) { - public DateNode(DateTimeOffset dateTime) - { - DateTime = dateTime; - } + DateTime = dateTime; + } - public DateTimeOffset DateTime { get; } + public DateTime DateTime { get; } - public override Expression BuildExpression(in BuildExpressionContext context) - => BuildOperation(context, Expression.Constant(DateTime.UtcDateTime, typeof(DateTime))); + public override Expression BuildExpression(in BuildExpressionContext context) + => BuildOperation(context, Expression.Constant(DateTime, typeof(DateTime))); - public override string ToString() - => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}{DateTime:o}"; - } + public override string ToString() + => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}{DateTime:o}"; +} - public class DateNode2 : OperatorNode +public class NowNode : OperatorNode +{ + public NowNode() { - public DateNode2(DateTime dateTime) - { - DateTime = dateTime; - } + } + public NowNode(long arithmetic) + { + Arithmetic = arithmetic; + } + + public long? Arithmetic { get; } - public DateTime DateTime { get; } + public override Expression BuildExpression(in BuildExpressionContext context) + => BuildOperation(context, Expression.Constant(context.UtcNow.AddDays(Arithmetic.GetValueOrDefault()), typeof(DateTime))); - public override Expression BuildExpression(in BuildExpressionContext context) - => BuildOperation(context, Expression.Constant(DateTime, typeof(DateTime))); + public override string ToString() + => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}@now{(Arithmetic.HasValue ? Arithmetic.Value.ToString() : string.Empty)}"; +} - public override string ToString() - => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}{DateTime:o}"; +public class TodayNode : NowNode +{ + public TodayNode() + { } - public class NowNode : OperatorNode + public TodayNode(long arithmetic) : base(arithmetic) { - public NowNode() - { - } - public NowNode(long arithmetic) - { - Arithmetic = arithmetic; - } + } - public long? Arithmetic { get; } + public override string ToString() + => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}@today{(Arithmetic.HasValue ? Arithmetic.Value.ToString() : string.Empty)}"; - public override Expression BuildExpression(in BuildExpressionContext context) - => BuildOperation(context, Expression.Constant(context.UtcNow.AddDays(Arithmetic.GetValueOrDefault()), typeof(DateTime))); +} - public override string ToString() - => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}@now{(Arithmetic.HasValue ? Arithmetic.Value.ToString() : string.Empty)}"; - } +public abstract class ExpressionNode : Node +{ } - public class TodayNode : NowNode +public class UnaryExpressionNode : ExpressionNode +{ + public UnaryExpressionNode(OperatorNode node) { - public TodayNode() - { - } + Node = node; + } - public TodayNode(long arithmetic) : base(arithmetic) - { - } + public override Expression BuildExpression(in BuildExpressionContext context) + => Expression.Lambda(context.Type, Node.BuildExpression(context), context.Parameter); - public override string ToString() - => $"{(string.IsNullOrEmpty(Operator) ? string.Empty : Operator)}@today{(Arithmetic.HasValue ? Arithmetic.Value.ToString() : string.Empty)}"; + public OperatorNode Node { get; } + public override string ToString() + => Node.ToString(); +} +public class BinaryExpressionNode : ExpressionNode +{ + public BinaryExpressionNode(OperatorNode left, OperatorNode right) + { + Left = left; + Right = right; } - public abstract class ExpressionNode : Node - { } + public OperatorNode Left { get; } + public OperatorNode Right { get; } - public class UnaryExpressionNode : ExpressionNode + public override Expression BuildExpression(in BuildExpressionContext context) { - public UnaryExpressionNode(OperatorNode node) - { - Node = node; - } - - public override Expression BuildExpression(in BuildExpressionContext context) - => Expression.Lambda(context.Type, Node.BuildExpression(context), context.Parameter); + var left = Expression.GreaterThanOrEqual(context.Member, Left.BuildExpression(context)); + var right = Expression.LessThanOrEqual(context.Member, Right.BuildExpression(context)); - public OperatorNode Node { get; } - public override string ToString() - => Node.ToString(); + return Expression.Lambda(context.Type, Expression.AndAlso(left, right), context.Parameter); } - public class BinaryExpressionNode : ExpressionNode - { - public BinaryExpressionNode(OperatorNode left, OperatorNode right) - { - Left = left; - Right = right; - } - - public OperatorNode Left { get; } - public OperatorNode Right { get; } - - public override Expression BuildExpression(in BuildExpressionContext context) - { - var left = Expression.GreaterThanOrEqual(context.Member, Left.BuildExpression(context)); - var right = Expression.LessThanOrEqual(context.Member, Right.BuildExpression(context)); - - return Expression.Lambda(context.Type, Expression.AndAlso(left, right), context.Parameter); - } + public override string ToString() => $"{Left}..{Right}"; +} - public override string ToString() => $"{Left}..{Right}"; - } +public static class DateTimeParser +{ + public static readonly Parser Parser; - public static class DateTimeParser + static DateTimeParser() { - public static readonly Parser Parser; - - static DateTimeParser() - { - var operators = OneOf(Literals.Text(">="), Literals.Text(">"), Literals.Text("<="), Literals.Text("<")); + var operators = OneOf(Literals.Text(">="), Literals.Text(">"), Literals.Text("<="), Literals.Text("<")); - var arithmetic = Terms.Integer(NumberOptions.AllowLeadingSign); - var range = Literals.Text(".."); + var arithmetic = Terms.Integer(NumberOptions.AllowLeadingSign); + var range = Literals.Text(".."); - var todayParser = Terms.Text("@today").And(ZeroOrOne(arithmetic)) - .Then(x => + var todayParser = Terms.Text("@today").And(ZeroOrOne(arithmetic)) + .Then(x => + { + if (x.Item2 != 0) { - if (x.Item2 != 0) - { - return new TodayNode(x.Item2); - } + return new TodayNode(x.Item2); + } - return new TodayNode(); - }); + return new TodayNode(); + }); - var nowParser = Terms.Text("@now").And(ZeroOrOne(arithmetic)) - .Then(x => + var nowParser = Terms.Text("@now").And(ZeroOrOne(arithmetic)) + .Then(x => + { + if (x.Item2 != 0) { - if (x.Item2 != 0) - { - return new NowNode(x.Item2); - } + return new NowNode(x.Item2); + } - return new NowNode(); - }); + return new NowNode(); + }); - var dateParser = AnyCharBefore(range) - .Then((ctx, x) => - { - var context = (DateTimeParseContext)ctx; - var dateValue = x.ToString(); + var dateParser = AnyCharBefore(range) + .Then((ctx, x) => + { + var context = (DateTimeParseContext)ctx; + var dateValue = x.ToString(); - // Try o, primarily for times, and fall back to local timezone. - if (DateTimeOffset.TryParseExact(dateValue, "yyyy-MM-dd'T'HH:mm:ss.FFFK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeOffset)) - { - return new DateNode2(dateTimeOffset.UtcDateTime); - } - else + // Try o, primarily for times, and fall back to local timezone. + if (DateTimeOffset.TryParseExact(dateValue, "yyyy-MM-dd'T'HH:mm:ss.FFFK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeOffset)) + { + return new DateNode2(dateTimeOffset.UtcDateTime); + } + else + { + var success = true; + if (!DateTime.TryParse(dateValue, context.CultureInfo, DateTimeStyles.None, out var dateTime)) { - var success = true; - if (!DateTime.TryParse(dateValue, context.CultureInfo, DateTimeStyles.None, out var dateTime)) + if (!DateTime.TryParse(dateValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) { - if (!DateTime.TryParse(dateValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) - { - success = false; - } + success = false; } + } - // If no timezone is specified, assume local using the configured timezone. - if (success) - { - var converted = context.Clock.ConvertToTimeZone(dateTime, context.UserTimeZone); - return new DateNode2(converted.UtcDateTime.Date); - } + // If no timezone is specified, assume local using the configured timezone. + if (success) + { + var converted = context.Clock.ConvertToTimeZone(dateTime, context.UserTimeZone); + return new DateNode2(converted.UtcDateTime.Date); } + } - throw new ParseException("Could not parse date", context.Scanner.Cursor.Position); - }); + throw new ParseException("Could not parse date", context.Scanner.Cursor.Position); + }); - var currentParser = OneOf(nowParser, todayParser); + var currentParser = OneOf(nowParser, todayParser); - var valueParser = OneOf(currentParser, dateParser); + var valueParser = OneOf(currentParser, dateParser); - var rangeParser = valueParser - .And(ZeroOrOne(range.SkipAnd(OneOf(currentParser, dateParser)))) - .Then(x => + var rangeParser = valueParser + .And(ZeroOrOne(range.SkipAnd(OneOf(currentParser, dateParser)))) + .Then(x => + { + if (x.Item2 == null) { - if (x.Item2 == null) - { - return new UnaryExpressionNode(x.Item1); - } + return new UnaryExpressionNode(x.Item1); + } - else - { - return new BinaryExpressionNode(x.Item1, x.Item2); - } - }); + else + { + return new BinaryExpressionNode(x.Item1, x.Item2); + } + }); - Parser = operators.And(valueParser) - .Then(x => - { - x.Item2.Operator = x.Item1; - return new UnaryExpressionNode(x.Item2); - }) - .Or(rangeParser).Compile(); - } + Parser = operators.And(valueParser) + .Then(x => + { + x.Item2.Operator = x.Item1; + return new UnaryExpressionNode(x.Item2); + }) + .Or(rangeParser).Compile(); } +} - public class DateTimeParseContext : ParseContext +public class DateTimeParseContext : ParseContext +{ + public DateTimeParseContext(CultureInfo cultureInfo, IClock clock, ITimeZone userTimeZone, Scanner scanner, bool useNewLines = false) : base(scanner, useNewLines) { - public DateTimeParseContext(CultureInfo cultureInfo, IClock clock, ITimeZone userTimeZone, Scanner scanner, bool useNewLines = false) : base(scanner, useNewLines) - { - CultureInfo = cultureInfo; - Clock = clock; - UserTimeZone = userTimeZone; - } - - public CultureInfo CultureInfo { get; set; } - public IClock Clock { get; set; } - public ITimeZone UserTimeZone { get; set; } + CultureInfo = cultureInfo; + Clock = clock; + UserTimeZone = userTimeZone; } + + public CultureInfo CultureInfo { get; set; } + public IClock Clock { get; set; } + public ITimeZone UserTimeZone { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterParser.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterParser.cs index 4e51f811bc6..261ecc25312 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterParser.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterParser.cs @@ -1,18 +1,17 @@ using OrchardCore.AuditTrail.Models; using YesSql.Filters.Query; -namespace OrchardCore.AuditTrail.Services -{ - public class DefaultAuditTrailAdminListFilterParser : IAuditTrailAdminListFilterParser - { - private readonly IQueryParser _parser; +namespace OrchardCore.AuditTrail.Services; - public DefaultAuditTrailAdminListFilterParser(IQueryParser parser) - { - _parser = parser; - } +public class DefaultAuditTrailAdminListFilterParser : IAuditTrailAdminListFilterParser +{ + private readonly IQueryParser _parser; - public QueryFilterResult Parse(string text) - => _parser.Parse(text); + public DefaultAuditTrailAdminListFilterParser(IQueryParser parser) + { + _parser = parser; } + + public QueryFilterResult Parse(string text) + => _parser.Parse(text); } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterProvider.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterProvider.cs index 768c13e9969..3f627f72732 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListFilterProvider.cs @@ -15,200 +15,199 @@ using YesSql.Filters.Query; using YesSql.Services; -namespace OrchardCore.AuditTrail.Services +namespace OrchardCore.AuditTrail.Services; + +public class DefaultAuditTrailAdminListFilterProvider : IAuditTrailAdminListFilterProvider { - public class DefaultAuditTrailAdminListFilterProvider : IAuditTrailAdminListFilterProvider + private readonly IOptions _options; + + public DefaultAuditTrailAdminListFilterProvider(IOptions options) { - private readonly IOptions _options; + _options = options; + } - public DefaultAuditTrailAdminListFilterProvider(IOptions options) - { - _options = options; - } + public void Build(QueryEngineBuilder builder) + { + builder + .WithNamedTerm("id", builder => builder + .OneCondition((val, query) => + { + if (!string.IsNullOrEmpty(val)) + { + query.With(x => x.CorrelationId == val); + } + + return query; + }) + .MapTo((val, model) => + { + model.CorrelationId = val; + }) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.CorrelationId)) + { + return (true, model.CorrelationId); + } + return (false, string.Empty); + }) + ) + .WithNamedTerm("category", builder => builder + .OneCondition((val, query) => + { + if (!string.IsNullOrEmpty(val)) + { + query.With(x => x.Category == val); + } + + return query; + }) + .MapTo((val, model) => + { + model.Category = val; + }) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.Category)) + { + return (true, model.Category); + } + return (false, string.Empty); + }) + ) + .WithNamedTerm("event", builder => builder + .OneCondition((val, query) => + { + if (!string.IsNullOrEmpty(val)) + { + query.With(x => x.Name == val); + } + + return query; + }) + .MapTo((val, model) => + { + model.Event = val; + }) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.Event)) + { + return (true, model.Event); + } + return (false, string.Empty); + }) + ) + .WithNamedTerm("date", builder => builder + .OneCondition(async (val, query, ctx) => + { + if (string.IsNullOrEmpty(val)) + { + return query; + } + + var context = (AuditTrailQueryContext)ctx; + var clock = context.ServiceProvider.GetRequiredService(); + var localClock = context.ServiceProvider.GetRequiredService(); + var userTimeZone = await localClock.GetLocalTimeZoneAsync(); + var parseContext = new DateTimeParseContext(CultureInfo.CurrentUICulture, clock, userTimeZone, new Scanner(val)); - public void Build(QueryEngineBuilder builder) - { - builder - .WithNamedTerm("id", builder => builder - .OneCondition((val, query) => + if (DateTimeParser.Parser.TryParse(parseContext, out var expression, out var parseError)) + { + var utcNow = clock.UtcNow; + + var param = Expression.Parameter(typeof(AuditTrailEventIndex)); + var field = Expression.Property(param, nameof(AuditTrailEventIndex.CreatedUtc)); + var expressionContext = new BuildExpressionContext(utcNow, param, field, typeof(Func)); + + query.With((Expression>)expression.BuildExpression(expressionContext)); + } + + return query; + }) + .MapTo((val, model) => + { + model.Date = val; + }) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.Date)) + { + return (true, model.Date); + } + return (false, string.Empty); + }) + ) + .WithNamedTerm("sort", builder => builder + .OneCondition((val, query, ctx) => + { + var context = (AuditTrailQueryContext)ctx; + var options = context.ServiceProvider.GetRequiredService>().Value; + + if (options.SortOptions.TryGetValue(val, out var sortOption)) + { + return sortOption.Query(val, query, ctx); + } + + return options.DefaultSortOption.Query(val, query, ctx); + }) + .MapTo((val, model) => + { + // TODO add a context property to the mapping func. + if (!string.IsNullOrEmpty(val) && _options.Value.SortOptions.TryGetValue(val, out var sortOption)) + { + model.Sort = sortOption.Value; + } + }) + .MapFrom((model) => + { + // TODO add a context property to the mapping func. + if (model.Sort != _options.Value.DefaultSortOption.Value) { - if (!string.IsNullOrEmpty(val)) - { - query.With(x => x.CorrelationId == val); - } + return (true, model.Sort); + } + + return (false, string.Empty); + }) + .AlwaysRun() + ) + .WithDefaultTerm("username", builder => builder + .ManyCondition( + (val, query, ctx) => + { + var context = (AuditTrailQueryContext)ctx; + var lookupNormalizer = context.ServiceProvider.GetRequiredService(); + var normalizedUserName = lookupNormalizer.NormalizeName(val); + query.With(x => x.NormalizedUserName.Contains(normalizedUserName)); - return query; - }) - .MapTo((val, model) => - { - model.CorrelationId = val; - }) - .MapFrom((model) => - { - if (!string.IsNullOrEmpty(model.CorrelationId)) - { - return (true, model.CorrelationId); - } - return (false, string.Empty); - }) - ) - .WithNamedTerm("category", builder => builder - .OneCondition((val, query) => + return new ValueTask>(query); + }, + (val, query, ctx) => { - if (!string.IsNullOrEmpty(val)) - { - query.With(x => x.Category == val); - } + var context = (AuditTrailQueryContext)ctx; + var lookupNormalizer = context.ServiceProvider.GetRequiredService(); + var normalizedUserName = lookupNormalizer.NormalizeName(val); + query.With(x => x.NormalizedUserName.NotContains(normalizedUserName)); - return query; - }) - .MapTo((val, model) => - { - model.Category = val; - }) - .MapFrom((model) => - { - if (!string.IsNullOrEmpty(model.Category)) - { - return (true, model.Category); - } - return (false, string.Empty); - }) + return new ValueTask>(query); + } ) - .WithNamedTerm("event", builder => builder - .OneCondition((val, query) => + ) + .WithNamedTerm("userid", builder => builder + .ManyCondition( + (val, query) => { - if (!string.IsNullOrEmpty(val)) - { - query.With(x => x.Name == val); - } + query.With(x => x.UserId.Contains(val)); return query; - }) - .MapTo((val, model) => - { - model.Event = val; - }) - .MapFrom((model) => - { - if (!string.IsNullOrEmpty(model.Event)) - { - return (true, model.Event); - } - return (false, string.Empty); - }) - ) - .WithNamedTerm("date", builder => builder - .OneCondition(async (val, query, ctx) => + }, + (val, query) => { - if (string.IsNullOrEmpty(val)) - { - return query; - } - - var context = (AuditTrailQueryContext)ctx; - var clock = context.ServiceProvider.GetRequiredService(); - var localClock = context.ServiceProvider.GetRequiredService(); - var userTimeZone = await localClock.GetLocalTimeZoneAsync(); - var parseContext = new DateTimeParseContext(CultureInfo.CurrentUICulture, clock, userTimeZone, new Scanner(val)); - - if (DateTimeParser.Parser.TryParse(parseContext, out var expression, out var parseError)) - { - var utcNow = clock.UtcNow; - - var param = Expression.Parameter(typeof(AuditTrailEventIndex)); - var field = Expression.Property(param, nameof(AuditTrailEventIndex.CreatedUtc)); - var expressionContext = new BuildExpressionContext(utcNow, param, field, typeof(Func)); - - query.With((Expression>)expression.BuildExpression(expressionContext)); - } + query.With(x => x.UserId.NotContains(val)); return query; - }) - .MapTo((val, model) => - { - model.Date = val; - }) - .MapFrom((model) => - { - if (!string.IsNullOrEmpty(model.Date)) - { - return (true, model.Date); - } - return (false, string.Empty); - }) - ) - .WithNamedTerm("sort", builder => builder - .OneCondition((val, query, ctx) => - { - var context = (AuditTrailQueryContext)ctx; - var options = context.ServiceProvider.GetRequiredService>().Value; - - if (options.SortOptions.TryGetValue(val, out var sortOption)) - { - return sortOption.Query(val, query, ctx); - } - - return options.DefaultSortOption.Query(val, query, ctx); - }) - .MapTo((val, model) => - { - // TODO add a context property to the mapping func. - if (!string.IsNullOrEmpty(val) && _options.Value.SortOptions.TryGetValue(val, out var sortOption)) - { - model.Sort = sortOption.Value; - } - }) - .MapFrom((model) => - { - // TODO add a context property to the mapping func. - if (model.Sort != _options.Value.DefaultSortOption.Value) - { - return (true, model.Sort); - } - - return (false, string.Empty); - }) - .AlwaysRun() - ) - .WithDefaultTerm("username", builder => builder - .ManyCondition( - (val, query, ctx) => - { - var context = (AuditTrailQueryContext)ctx; - var lookupNormalizer = context.ServiceProvider.GetRequiredService(); - var normalizedUserName = lookupNormalizer.NormalizeName(val); - query.With(x => x.NormalizedUserName.Contains(normalizedUserName)); - - return new ValueTask>(query); - }, - (val, query, ctx) => - { - var context = (AuditTrailQueryContext)ctx; - var lookupNormalizer = context.ServiceProvider.GetRequiredService(); - var normalizedUserName = lookupNormalizer.NormalizeName(val); - query.With(x => x.NormalizedUserName.NotContains(normalizedUserName)); - - return new ValueTask>(query); - } - ) + } ) - .WithNamedTerm("userid", builder => builder - .ManyCondition( - (val, query) => - { - query.With(x => x.UserId.Contains(val)); - - return query; - }, - (val, query) => - { - query.With(x => x.UserId.NotContains(val)); - - return query; - } - ) - ); - } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListQueryService.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListQueryService.cs index 63db00d7ef4..76bee39b42e 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListQueryService.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Services/DefaultAuditTrailAdminListQueryService.cs @@ -11,134 +11,133 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.AuditTrail.Services +namespace OrchardCore.AuditTrail.Services; + +public class DefaultAuditTrailAdminListQueryService : IAuditTrailAdminListQueryService { - public class DefaultAuditTrailAdminListQueryService : IAuditTrailAdminListQueryService + private const string DateFormat = "yyyy-MM-dd"; + private readonly IAuditTrailManager _auditTrailManager; + private readonly ILocalClock _localClock; + private readonly AuditTrailAdminListOptions _adminListOptions; + private readonly ISession _session; + private readonly IServiceProvider _serviceProvider; + protected readonly IStringLocalizer S; + + public DefaultAuditTrailAdminListQueryService( + IAuditTrailManager auditTrailManager, + ILocalClock localClock, + IOptions adminListOptions, + ISession session, + IServiceProvider serviceProvider, + IStringLocalizer stringLocalizer) { - private const string DateFormat = "yyyy-MM-dd"; - private readonly IAuditTrailManager _auditTrailManager; - private readonly ILocalClock _localClock; - private readonly AuditTrailAdminListOptions _adminListOptions; - private readonly ISession _session; - private readonly IServiceProvider _serviceProvider; - protected readonly IStringLocalizer S; - - public DefaultAuditTrailAdminListQueryService( - IAuditTrailManager auditTrailManager, - ILocalClock localClock, - IOptions adminListOptions, - ISession session, - IServiceProvider serviceProvider, - IStringLocalizer stringLocalizer) - { - _auditTrailManager = auditTrailManager; - _localClock = localClock; - _adminListOptions = adminListOptions.Value; - _session = session; - _serviceProvider = serviceProvider; - S = stringLocalizer; - } + _auditTrailManager = auditTrailManager; + _localClock = localClock; + _adminListOptions = adminListOptions.Value; + _session = session; + _serviceProvider = serviceProvider; + S = stringLocalizer; + } - public async Task QueryAsync(int page, int pageSize, AuditTrailIndexOptions options) - { - // Because admin filters can add a different index to the query this must be added as a Query() - var query = _session.Query(collection: AuditTrailEvent.Collection); + public async Task QueryAsync(int page, int pageSize, AuditTrailIndexOptions options) + { + // Because admin filters can add a different index to the query this must be added as a Query() + var query = _session.Query(collection: AuditTrailEvent.Collection); - query = await options.FilterResult.ExecuteAsync(new AuditTrailQueryContext(_serviceProvider, query)); + query = await options.FilterResult.ExecuteAsync(new AuditTrailQueryContext(_serviceProvider, query)); - var totalCount = await query.CountAsync(); + var totalCount = await query.CountAsync(); - if (pageSize > 0) + if (pageSize > 0) + { + if (page > 1) { - if (page > 1) - { - query = query.Skip((page - 1) * pageSize); - } - - query = query.Take(pageSize); + query = query.Skip((page - 1) * pageSize); } - var events = await query.ListAsync(); + query = query.Take(pageSize); + } - var result = new AuditTrailEventQueryResult - { - Events = events, - TotalCount = totalCount - }; + var events = await query.ListAsync(); + + var result = new AuditTrailEventQueryResult + { + Events = events, + TotalCount = totalCount + }; - options.AuditTrailSorts = _adminListOptions.SortOptions.Values.Where(x => x.SelectListItem != null).Select(opt => opt.SelectListItem(_serviceProvider, opt, options)).ToList(); + options.AuditTrailSorts = _adminListOptions.SortOptions.Values.Where(x => x.SelectListItem != null).Select(opt => opt.SelectListItem(_serviceProvider, opt, options)).ToList(); - var categories = _auditTrailManager.DescribeCategories(); + var categories = _auditTrailManager.DescribeCategories(); - options.Categories = categories - .Select(category => new SelectListItem(category.LocalizedName(_serviceProvider), category.Name, category.Name == options.Category)) - .ToList(); + options.Categories = categories + .Select(category => new SelectListItem(category.LocalizedName(_serviceProvider), category.Name, category.Name == options.Category)) + .ToList(); - options.Categories.Insert(0, new SelectListItem(S["All categories"], string.Empty, string.IsNullOrEmpty(options.Category))); + options.Categories.Insert(0, new SelectListItem(S["All categories"], string.Empty, string.IsNullOrEmpty(options.Category))); - if (options.CorrelationIdFromRoute) + if (options.CorrelationIdFromRoute) + { + var firstEvent = result.Events.FirstOrDefault(); + if (firstEvent != null) { - var firstEvent = result.Events.FirstOrDefault(); - if (firstEvent != null) + var currentCategory = categories.FirstOrDefault(x => x.Name == firstEvent.Category); + if (currentCategory != null) { - var currentCategory = categories.FirstOrDefault(x => x.Name == firstEvent.Category); - if (currentCategory != null) - { - options.Events = currentCategory.Events.Values.Select(category => - new SelectListItem(category.LocalizedName(_serviceProvider), category.Name, category.Name == options.Category)).ToList(); - } + options.Events = currentCategory.Events.Values.Select(category => + new SelectListItem(category.LocalizedName(_serviceProvider), category.Name, category.Name == options.Category)).ToList(); } } + } - var localNow = await _localClock.LocalNowAsync; - var startOfWeek = CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek; + var localNow = await _localClock.LocalNowAsync; + var startOfWeek = CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek; - options.AuditTrailDates = - [ - new(S["Any date"], string.Empty, options.Date == string.Empty), - ]; + options.AuditTrailDates = + [ + new(S["Any date"], string.Empty, options.Date == string.Empty), + ]; - var dateTimeValue = ">@now-1"; - options.AuditTrailDates.Add(new SelectListItem(S["Last 24 hours"], dateTimeValue, options.Date == dateTimeValue)); + var dateTimeValue = ">@now-1"; + options.AuditTrailDates.Add(new SelectListItem(S["Last 24 hours"], dateTimeValue, options.Date == dateTimeValue)); - dateTimeValue = "@now-2..@now-1"; - options.AuditTrailDates.Add(new SelectListItem(S["Previous 48 hours"], dateTimeValue, options.Date == dateTimeValue)); + dateTimeValue = "@now-2..@now-1"; + options.AuditTrailDates.Add(new SelectListItem(S["Previous 48 hours"], dateTimeValue, options.Date == dateTimeValue)); - dateTimeValue = $">{localNow.StartOfWeek(CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek).LocalDateTime.ToString(DateFormat)}"; - options.AuditTrailDates.Add(new SelectListItem(S["This week"], dateTimeValue, options.Date == dateTimeValue)); + dateTimeValue = $">{localNow.StartOfWeek(CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek).LocalDateTime.ToString(DateFormat)}"; + options.AuditTrailDates.Add(new SelectListItem(S["This week"], dateTimeValue, options.Date == dateTimeValue)); - var start = localNow.StartOfWeek(CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek).AddDays(-7).LocalDateTime; - var end = start.AddDays(7); - dateTimeValue = $"{start.ToString(DateFormat)}..{end.ToString(DateFormat)}"; - options.AuditTrailDates.Add(new SelectListItem(S["Last week"], dateTimeValue, options.Date == dateTimeValue)); + var start = localNow.StartOfWeek(CultureInfo.CurrentUICulture.DateTimeFormat.FirstDayOfWeek).AddDays(-7).LocalDateTime; + var end = start.AddDays(7); + dateTimeValue = $"{start.ToString(DateFormat)}..{end.ToString(DateFormat)}"; + options.AuditTrailDates.Add(new SelectListItem(S["Last week"], dateTimeValue, options.Date == dateTimeValue)); - start = new DateTime(localNow.LocalDateTime.Year, localNow.LocalDateTime.Month, 1); - dateTimeValue = $">{start.ToString(DateFormat)}"; - options.AuditTrailDates.Add(new SelectListItem(S["This month"], dateTimeValue, options.Date == dateTimeValue)); + start = new DateTime(localNow.LocalDateTime.Year, localNow.LocalDateTime.Month, 1); + dateTimeValue = $">{start.ToString(DateFormat)}"; + options.AuditTrailDates.Add(new SelectListItem(S["This month"], dateTimeValue, options.Date == dateTimeValue)); - start = new DateTime(localNow.LocalDateTime.Year, localNow.LocalDateTime.Month, 1).AddMonths(-1); - end = start.AddMonths(1); - dateTimeValue = $"{start.ToString(DateFormat)}..{end.ToString(DateFormat)}"; - options.AuditTrailDates.Add(new SelectListItem(S["Last month"], dateTimeValue, options.Date == dateTimeValue)); - options.AuditTrailDates.Add(new SelectListItem(S["Custom date range"], dateTimeValue, options.Date == dateTimeValue)); + start = new DateTime(localNow.LocalDateTime.Year, localNow.LocalDateTime.Month, 1).AddMonths(-1); + end = start.AddMonths(1); + dateTimeValue = $"{start.ToString(DateFormat)}..{end.ToString(DateFormat)}"; + options.AuditTrailDates.Add(new SelectListItem(S["Last month"], dateTimeValue, options.Date == dateTimeValue)); + options.AuditTrailDates.Add(new SelectListItem(S["Custom date range"], dateTimeValue, options.Date == dateTimeValue)); - dateTimeValue = $">{localNow.AddHours(-1):o}"; - options.AuditTrailDates.Add(new SelectListItem(S["Last hour"], dateTimeValue, options.Date == dateTimeValue)); + dateTimeValue = $">{localNow.AddHours(-1):o}"; + options.AuditTrailDates.Add(new SelectListItem(S["Last hour"], dateTimeValue, options.Date == dateTimeValue)); - dateTimeValue = $"{localNow.AddHours(-2):o}..{localNow.AddHours(-1):o}"; - options.AuditTrailDates.Add(new SelectListItem(S["Previous hour"], dateTimeValue, options.Date == dateTimeValue)); - options.AuditTrailDates.Add(new SelectListItem(S["Custom time range"], dateTimeValue, options.Date == dateTimeValue)); + dateTimeValue = $"{localNow.AddHours(-2):o}..{localNow.AddHours(-1):o}"; + options.AuditTrailDates.Add(new SelectListItem(S["Previous hour"], dateTimeValue, options.Date == dateTimeValue)); + options.AuditTrailDates.Add(new SelectListItem(S["Custom time range"], dateTimeValue, options.Date == dateTimeValue)); - return result; - } + return result; } +} - public static class DateTimeExtensions +public static class DateTimeExtensions +{ + public static DateTimeOffset StartOfWeek(this DateTimeOffset dt, DayOfWeek startOfWeek) { - public static DateTimeOffset StartOfWeek(this DateTimeOffset dt, DayOfWeek startOfWeek) - { - var diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7; - return dt.AddDays(-1 * diff).Date; - } + var diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7; + return dt.AddDays(-1 * diff).Date; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailCategorySettings.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailCategorySettings.cs index 76f2e463b8b..e33cfe74566 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailCategorySettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailCategorySettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.AuditTrail.Settings +namespace OrchardCore.AuditTrail.Settings; + +public class AuditTrailCategorySettings { - public class AuditTrailCategorySettings - { - public string Name { get; set; } - public AuditTrailEventSettings[] Events { get; set; } = []; - } + public string Name { get; set; } + public AuditTrailEventSettings[] Events { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailEventSettings.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailEventSettings.cs index 627fe2074de..8a5af062311 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailEventSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailEventSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.AuditTrail.Settings +namespace OrchardCore.AuditTrail.Settings; + +public class AuditTrailEventSettings { - public class AuditTrailEventSettings - { - public string Name { get; set; } - public string Category { get; set; } - public bool IsEnabled { get; set; } - } + public string Name { get; set; } + public string Category { get; set; } + public bool IsEnabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailSettings.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailSettings.cs index e465a11eeac..59c1388a467 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.AuditTrail.Settings +namespace OrchardCore.AuditTrail.Settings; + +public class AuditTrailSettings { - public class AuditTrailSettings - { - public AuditTrailCategorySettings[] Categories { get; set; } = []; - public bool ClientIpAddressAllowed { get; set; } - } + public AuditTrailCategorySettings[] Categories { get; set; } = []; + public bool ClientIpAddressAllowed { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailTrimmingSettings.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailTrimmingSettings.cs index f0aeb420297..06448ea554d 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailTrimmingSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Settings/AuditTrailTrimmingSettings.cs @@ -1,11 +1,10 @@ using System; -namespace OrchardCore.AuditTrail.Settings +namespace OrchardCore.AuditTrail.Settings; + +public class AuditTrailTrimmingSettings { - public class AuditTrailTrimmingSettings - { - public int RetentionDays { get; set; } = 10; - public DateTime? LastRunUtc { get; set; } - public bool Disabled { get; set; } - } + public int RetentionDays { get; set; } = 10; + public DateTime? LastRunUtc { get; set; } + public bool Disabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Startup.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Startup.cs index c6084e37dad..684789e0ff1 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/Startup.cs @@ -21,115 +21,114 @@ using OrchardCore.Settings.Deployment; using YesSql.Filters.Query; -namespace OrchardCore.AuditTrail +namespace OrchardCore.AuditTrail; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Add ILookupNormalizer as Singleton because it is needed by Users - services.TryAddSingleton(); + // Add ILookupNormalizer as Singleton because it is needed by Users + services.TryAddSingleton(); - services.AddScoped(); + services.AddScoped(); - services - .AddScoped, AuditTrailEventDisplayDriver>(); + services + .AddScoped, AuditTrailEventDisplayDriver>(); - services.AddSingleton(); + services.AddSingleton(); - services.Configure(o => o.Collections.Add(AuditTrailEvent.Collection)); + services.Configure(o => o.Collections.Add(AuditTrailEvent.Collection)); - services.AddDataMigration(); - services.AddIndexProvider(); - services.AddSingleton(); + services.AddDataMigration(); + services.AddIndexProvider(); + services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddScoped, AuditTrailSettingsDisplayDriver>(); - services.AddScoped, AuditTrailTrimmingSettingsDisplayDriver>(); + services.AddScoped, AuditTrailSettingsDisplayDriver>(); + services.AddScoped, AuditTrailTrimmingSettingsDisplayDriver>(); - services.AddScoped, AuditTrailOptionsDisplayDriver>(); + services.AddScoped, AuditTrailOptionsDisplayDriver>(); - services.AddScoped(); + services.AddScoped(); - services.AddSingleton(sp => + services.AddSingleton(sp => + { + var filterProviders = sp.GetServices(); + var builder = new QueryEngineBuilder(); + foreach (var provider in filterProviders) { - var filterProviders = sp.GetServices(); - var builder = new QueryEngineBuilder(); - foreach (var provider in filterProviders) - { - provider.Build(builder); - } + provider.Build(builder); + } - var parser = builder.Build(); + var parser = builder.Build(); - return new DefaultAuditTrailAdminListFilterParser(parser); - }); + return new DefaultAuditTrailAdminListFilterParser(parser); + }); - services.AddTransient(); + services.AddTransient(); - services.AddOptions(); + services.AddOptions(); - services.Configure(options => - { - options - .ForSort("time-desc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.CreatedUtc)) - .WithSelectListItem((S, opt, model) => new SelectListItem(S["Newest"], opt.Value, model.Sort == string.Empty)) - .AsDefault()) + services.Configure(options => + { + options + .ForSort("time-desc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.CreatedUtc)) + .WithSelectListItem((S, opt, model) => new SelectListItem(S["Newest"], opt.Value, model.Sort == string.Empty)) + .AsDefault()) - .ForSort("time-asc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.CreatedUtc)) - .WithSelectListItem((S, opt, model) => new SelectListItem(S["Oldest"], opt.Value, model.Sort == opt.Value))) + .ForSort("time-asc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.CreatedUtc)) + .WithSelectListItem((S, opt, model) => new SelectListItem(S["Oldest"], opt.Value, model.Sort == opt.Value))) - .ForSort("category-asc-time-desc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.Category).ThenByDescending(i => i.CreatedUtc)) - .WithSelectListItem((S, opt, model) => new SelectListItem(S["Category"], opt.Value, model.Sort == opt.Value))) + .ForSort("category-asc-time-desc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.Category).ThenByDescending(i => i.CreatedUtc)) + .WithSelectListItem((S, opt, model) => new SelectListItem(S["Category"], opt.Value, model.Sort == opt.Value))) - .ForSort("category-asc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.Category).ThenBy(i => i.CreatedUtc))) + .ForSort("category-asc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.Category).ThenBy(i => i.CreatedUtc))) - .ForSort("category-desc-time-desc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.Category).ThenByDescending(i => i.CreatedUtc))) + .ForSort("category-desc-time-desc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.Category).ThenByDescending(i => i.CreatedUtc))) - .ForSort("category-desc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.Category).ThenBy(i => i.CreatedUtc))) + .ForSort("category-desc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.Category).ThenBy(i => i.CreatedUtc))) - .ForSort("event-asc-time-desc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.Name).ThenByDescending(i => i.CreatedUtc)) - .WithSelectListItem((S, opt, model) => new SelectListItem(S["Event"], opt.Value, model.Sort == opt.Value))) + .ForSort("event-asc-time-desc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.Name).ThenByDescending(i => i.CreatedUtc)) + .WithSelectListItem((S, opt, model) => new SelectListItem(S["Event"], opt.Value, model.Sort == opt.Value))) - .ForSort("event-asc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.Name).ThenBy(i => i.CreatedUtc))) + .ForSort("event-asc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.Name).ThenBy(i => i.CreatedUtc))) - .ForSort("event-desc-time-desc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.Name).ThenByDescending(i => i.CreatedUtc))) + .ForSort("event-desc-time-desc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.Name).ThenByDescending(i => i.CreatedUtc))) - .ForSort("event-desc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.Name).ThenBy(i => i.CreatedUtc))) + .ForSort("event-desc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.Name).ThenBy(i => i.CreatedUtc))) - .ForSort("user-asc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderBy(i => i.NormalizedUserName).ThenBy(i => i.CreatedUtc)) - .WithSelectListItem((S, opt, model) => new SelectListItem(S["User"], opt.Value, model.Sort == opt.Value))) + .ForSort("user-asc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderBy(i => i.NormalizedUserName).ThenBy(i => i.CreatedUtc)) + .WithSelectListItem((S, opt, model) => new SelectListItem(S["User"], opt.Value, model.Sort == opt.Value))) - .ForSort("user-desc-time-desc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.NormalizedUserName).ThenByDescending(i => i.CreatedUtc))) + .ForSort("user-desc-time-desc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.NormalizedUserName).ThenByDescending(i => i.CreatedUtc))) - .ForSort("user-desc-time-asc", b => b - .WithQuery((val, query) => query.With().OrderByDescending(i => i.NormalizedUserName).ThenBy(i => i.CreatedUtc))); - }); - } + .ForSort("user-desc-time-asc", b => b + .WithQuery((val, query) => query.With().OrderByDescending(i => i.NormalizedUserName).ThenBy(i => i.CreatedUtc))); + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Audit Trail settings"], S => S["Exports the audit trail settings."]); - services.AddSiteSettingsPropertyDeploymentStep(S => S["Audit Trail Trimming settings"], S => S["Exports the audit trail trimming settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Audit Trail settings"], S => S["Exports the audit trail settings."]); + services.AddSiteSettingsPropertyDeploymentStep(S => S["Audit Trail Trimming settings"], S => S["Exports the audit trail trimming settings."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailCategorySettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailCategorySettingsViewModel.cs index 45e9bc6503b..c0b084ac40e 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailCategorySettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailCategorySettingsViewModel.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Localization; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailCategorySettingsViewModel { - public class AuditTrailCategorySettingsViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public AuditTrailEventSettingsViewModel[] Events { get; set; } + public AuditTrailEventSettingsViewModel[] Events { get; set; } - [BindNever] - public LocalizedString LocalizedName { get; set; } - } + [BindNever] + public LocalizedString LocalizedName { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventSettingsViewModel.cs index 6aaad04ee96..f3b8cbbe64b 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventSettingsViewModel.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Localization; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailEventSettingsViewModel { - public class AuditTrailEventSettingsViewModel - { - public string Name { get; set; } - public string Category { get; set; } - public bool IsEnabled { get; set; } - public bool IsMandatory { get; set; } + public string Name { get; set; } + public string Category { get; set; } + public bool IsEnabled { get; set; } + public bool IsMandatory { get; set; } - [BindNever] - public LocalizedString LocalizedName { get; set; } + [BindNever] + public LocalizedString LocalizedName { get; set; } - [BindNever] - public LocalizedString Description { get; set; } - } + [BindNever] + public LocalizedString Description { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventViewModel.cs index 62ebf4c8cc0..3d6f2267246 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailEventViewModel.cs @@ -1,11 +1,10 @@ using OrchardCore.AuditTrail.Models; using OrchardCore.AuditTrail.Services.Models; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailEventViewModel { - public class AuditTrailEventViewModel - { - public AuditTrailEvent AuditTrailEvent { get; set; } - public AuditTrailEventDescriptor Descriptor { get; set; } - } + public AuditTrailEvent AuditTrailEvent { get; set; } + public AuditTrailEventDescriptor Descriptor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailItemViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailItemViewModel.cs index 11232a63dfc..1125eab8482 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailItemViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailItemViewModel.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.DisplayManagement; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailItemViewModel { - public class AuditTrailItemViewModel - { - [BindNever] - public IShape Shape { get; set; } - } + [BindNever] + public IShape Shape { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailListViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailListViewModel.cs index d57063415aa..df8f92b4f23 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailListViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailListViewModel.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using OrchardCore.DisplayManagement; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailListViewModel { - public class AuditTrailListViewModel - { - public IList Events { get; set; } - public AuditTrailIndexOptions Options { get; set; } = new AuditTrailIndexOptions(); - public IShape Pager { get; set; } - public dynamic Header { get; set; } - } + public IList Events { get; set; } + public AuditTrailIndexOptions Options { get; set; } = new AuditTrailIndexOptions(); + public IShape Pager { get; set; } + public dynamic Header { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs index 397b2aa8d72..b6663d162c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailSettingsViewModel { - public class AuditTrailSettingsViewModel - { - public AuditTrailCategorySettingsViewModel[] Categories { get; set; } = []; - public bool ClientIpAddressAllowed { get; set; } - } + public AuditTrailCategorySettingsViewModel[] Categories { get; set; } = []; + public bool ClientIpAddressAllowed { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailTrimmingSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailTrimmingSettingsViewModel.cs index 9aab614111c..427fe4b3610 100644 --- a/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailTrimmingSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AuditTrail/ViewModels/AuditTrailTrimmingSettingsViewModel.cs @@ -1,11 +1,10 @@ using System; -namespace OrchardCore.AuditTrail.ViewModels +namespace OrchardCore.AuditTrail.ViewModels; + +public class AuditTrailTrimmingSettingsViewModel { - public class AuditTrailTrimmingSettingsViewModel - { - public int RetentionDays { get; set; } - public DateTime? LastRunUtc { get; set; } - public bool Disabled { get; set; } - } + public int RetentionDays { get; set; } + public DateTime? LastRunUtc { get; set; } + public bool Disabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs index fc639cba1f6..1bf81a1368f 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/AutoSetupMiddleware.cs @@ -14,260 +14,259 @@ using OrchardCore.Locking.Distributed; using OrchardCore.Setup.Services; -namespace OrchardCore.AutoSetup +namespace OrchardCore.AutoSetup; + +/// +/// The auto setup middleware. +/// +public class AutoSetupMiddleware { /// - /// The auto setup middleware. + /// The next middleware in the execution pipeline. + /// + private readonly RequestDelegate _next; + + /// + /// The shell host. + /// + private readonly IShellHost _shellHost; + + /// + /// The shell settings. + /// + private readonly ShellSettings _shellSettings; + + /// + /// The shell settings manager. /// - public class AutoSetupMiddleware + private readonly IShellSettingsManager _shellSettingsManager; + + /// + /// A distributed lock guaranties an atomic setup in multi instances environment. + /// + private readonly IDistributedLock _distributedLock; + + /// + /// The auto setup options. + /// + private readonly AutoSetupOptions _options; + + /// + /// The logger. + /// + private readonly ILogger _logger; + + /// + /// The auto setup lock options. + /// + private readonly LockOptions _lockOptions; + + /// + /// The tenant setup options. + /// + private readonly TenantSetupOptions _setupOptions; + + /// + /// Initializes a new instance of the class. + /// + /// The next middleware in the execution pipeline. + /// The shell host. + /// The shell settings. + /// The shell settings manager. + /// The distributed lock. + /// The auto setup options. + /// The logger. + public AutoSetupMiddleware( + RequestDelegate next, + IShellHost shellHost, + ShellSettings shellSettings, + IShellSettingsManager shellSettingsManager, + IDistributedLock distributedLock, + IOptions options, + ILogger logger) { - /// - /// The next middleware in the execution pipeline. - /// - private readonly RequestDelegate _next; - - /// - /// The shell host. - /// - private readonly IShellHost _shellHost; - - /// - /// The shell settings. - /// - private readonly ShellSettings _shellSettings; - - /// - /// The shell settings manager. - /// - private readonly IShellSettingsManager _shellSettingsManager; - - /// - /// A distributed lock guaranties an atomic setup in multi instances environment. - /// - private readonly IDistributedLock _distributedLock; - - /// - /// The auto setup options. - /// - private readonly AutoSetupOptions _options; - - /// - /// The logger. - /// - private readonly ILogger _logger; - - /// - /// The auto setup lock options. - /// - private readonly LockOptions _lockOptions; - - /// - /// The tenant setup options. - /// - private readonly TenantSetupOptions _setupOptions; - - /// - /// Initializes a new instance of the class. - /// - /// The next middleware in the execution pipeline. - /// The shell host. - /// The shell settings. - /// The shell settings manager. - /// The distributed lock. - /// The auto setup options. - /// The logger. - public AutoSetupMiddleware( - RequestDelegate next, - IShellHost shellHost, - ShellSettings shellSettings, - IShellSettingsManager shellSettingsManager, - IDistributedLock distributedLock, - IOptions options, - ILogger logger) - { - _next = next; - _shellHost = shellHost; - _shellSettings = shellSettings; - _shellSettingsManager = shellSettingsManager; - _distributedLock = distributedLock; - _options = options.Value; - _logger = logger; - - _lockOptions = _options.LockOptions; - _setupOptions = _options.Tenants.FirstOrDefault(options => _shellSettings.Name == options.ShellName); - } + _next = next; + _shellHost = shellHost; + _shellSettings = shellSettings; + _shellSettingsManager = shellSettingsManager; + _distributedLock = distributedLock; + _options = options.Value; + _logger = logger; + + _lockOptions = _options.LockOptions; + _setupOptions = _options.Tenants.FirstOrDefault(options => _shellSettings.Name == options.ShellName); + } - /// - /// The auto setup middleware invoke. - /// - /// - /// The http context. - /// - /// - /// The . - /// - public async Task InvokeAsync(HttpContext httpContext) + /// + /// The auto setup middleware invoke. + /// + /// + /// The http context. + /// + /// + /// The . + /// + public async Task InvokeAsync(HttpContext httpContext) + { + if (_setupOptions is not null && _shellSettings.IsUninitialized()) { - if (_setupOptions is not null && _shellSettings.IsUninitialized()) + // Try to acquire a lock before starting installation, it guaranties an atomic setup in multi instances environment. + (var locker, var locked) = await _distributedLock.TryAcquireAutoSetupLockAsync(_lockOptions); + if (!locked) { - // Try to acquire a lock before starting installation, it guaranties an atomic setup in multi instances environment. - (var locker, var locked) = await _distributedLock.TryAcquireAutoSetupLockAsync(_lockOptions); - if (!locked) - { - throw new TimeoutException($"Fails to acquire an auto setup lock for the tenant: {_setupOptions.ShellName}"); - } + throw new TimeoutException($"Fails to acquire an auto setup lock for the tenant: {_setupOptions.ShellName}"); + } - await using var acquiredLock = locker; + await using var acquiredLock = locker; - if (_shellSettings.IsUninitialized()) + if (_shellSettings.IsUninitialized()) + { + var pathBase = httpContext.Request.PathBase; + if (!pathBase.HasValue) { - var pathBase = httpContext.Request.PathBase; - if (!pathBase.HasValue) - { - pathBase = "/"; - } + pathBase = "/"; + } - // Check if the tenant was installed by another instance. - using var settings = (await _shellSettingsManager - .LoadSettingsAsync(_shellSettings.Name)) - .AsDisposable(); + // Check if the tenant was installed by another instance. + using var settings = (await _shellSettingsManager + .LoadSettingsAsync(_shellSettings.Name)) + .AsDisposable(); - if (!settings.IsUninitialized()) - { - await _shellHost.ReloadShellContextAsync(_shellSettings, eventSource: false); - httpContext.Response.Redirect(pathBase); + if (!settings.IsUninitialized()) + { + await _shellHost.ReloadShellContextAsync(_shellSettings, eventSource: false); + httpContext.Response.Redirect(pathBase); - return; - } + return; + } - var setupService = httpContext.RequestServices.GetRequiredService(); - if (await SetupTenantAsync(setupService, _setupOptions, _shellSettings)) + var setupService = httpContext.RequestServices.GetRequiredService(); + if (await SetupTenantAsync(setupService, _setupOptions, _shellSettings)) + { + if (_setupOptions.IsDefault) { - if (_setupOptions.IsDefault) + // Create the rest of the shells for further on demand setup. + foreach (var setupOptions in _options.Tenants) { - // Create the rest of the shells for further on demand setup. - foreach (var setupOptions in _options.Tenants) + if (_setupOptions != setupOptions) { - if (_setupOptions != setupOptions) - { - await CreateTenantSettingsAsync(setupOptions); - } + await CreateTenantSettingsAsync(setupOptions); } } + } - httpContext.Response.Redirect(pathBase); + httpContext.Response.Redirect(pathBase); - return; - } + return; } } - - await _next.Invoke(httpContext); } - /// - /// Sets up a tenant. - /// - /// The setup service. - /// The tenant setup options. - /// The tenant shell settings. - /// - /// Returns true if successfully setup. - /// - public async Task SetupTenantAsync(ISetupService setupService, TenantSetupOptions setupOptions, ShellSettings shellSettings) - { - var setupContext = await GetSetupContextAsync(setupOptions, setupService, shellSettings); - - _logger.LogInformation("AutoSetup is initializing the site"); + await _next.Invoke(httpContext); + } - await setupService.SetupAsync(setupContext); + /// + /// Sets up a tenant. + /// + /// The setup service. + /// The tenant setup options. + /// The tenant shell settings. + /// + /// Returns true if successfully setup. + /// + public async Task SetupTenantAsync(ISetupService setupService, TenantSetupOptions setupOptions, ShellSettings shellSettings) + { + var setupContext = await GetSetupContextAsync(setupOptions, setupService, shellSettings); - if (setupContext.Errors.Count == 0) - { - _logger.LogInformation("AutoSetup successfully provisioned the site '{SiteName}'.", setupOptions.SiteName); + _logger.LogInformation("AutoSetup is initializing the site"); - return true; - } + await setupService.SetupAsync(setupContext); - var stringBuilder = new StringBuilder(); - foreach (var error in setupContext.Errors) - { - stringBuilder.AppendLine($"{error.Key} : '{error.Value}'"); - } - - _logger.LogError("AutoSetup failed installing the site '{SiteName}' with errors: {Errors}", setupOptions.SiteName, stringBuilder); + if (setupContext.Errors.Count == 0) + { + _logger.LogInformation("AutoSetup successfully provisioned the site '{SiteName}'.", setupOptions.SiteName); - return false; + return true; } - /// - /// Creates a tenant shell settings. - /// - /// The setup options. - /// The . - public async Task CreateTenantSettingsAsync(TenantSetupOptions setupOptions) + var stringBuilder = new StringBuilder(); + foreach (var error in setupContext.Errors) { - using var shellSettings = _shellSettingsManager - .CreateDefaultSettings() - .AsUninitialized() - .AsDisposable(); - - shellSettings.Name = setupOptions.ShellName; - shellSettings.RequestUrlHost = setupOptions.RequestUrlHost; - shellSettings.RequestUrlPrefix = setupOptions.RequestUrlPrefix; - - shellSettings["ConnectionString"] = setupOptions.DatabaseConnectionString; - shellSettings["TablePrefix"] = setupOptions.DatabaseTablePrefix; - shellSettings["Schema"] = setupOptions.DatabaseSchema; - shellSettings["DatabaseProvider"] = setupOptions.DatabaseProvider; - shellSettings["Secret"] = Guid.NewGuid().ToString(); - shellSettings["RecipeName"] = setupOptions.RecipeName; - shellSettings["FeatureProfile"] = setupOptions.FeatureProfile; - - await _shellHost.UpdateShellSettingsAsync(shellSettings); - - return shellSettings; + stringBuilder.AppendLine($"{error.Key} : '{error.Value}'"); } - /// - /// Gets a setup context from the configuration. - /// - /// The tenant setup options. - /// The setup service. - /// The tenant shell settings. - /// The used to setup the site. - private static async Task GetSetupContextAsync(TenantSetupOptions options, ISetupService setupService, ShellSettings shellSettings) - { - var recipes = await setupService.GetSetupRecipesAsync(); + _logger.LogError("AutoSetup failed installing the site '{SiteName}' with errors: {Errors}", setupOptions.SiteName, stringBuilder); - var recipe = recipes.SingleOrDefault(r => r.Name == options.RecipeName); + return false; + } - var setupContext = new SetupContext - { - Recipe = recipe, - ShellSettings = shellSettings, - Errors = new Dictionary() - }; + /// + /// Creates a tenant shell settings. + /// + /// The setup options. + /// The . + public async Task CreateTenantSettingsAsync(TenantSetupOptions setupOptions) + { + using var shellSettings = _shellSettingsManager + .CreateDefaultSettings() + .AsUninitialized() + .AsDisposable(); + + shellSettings.Name = setupOptions.ShellName; + shellSettings.RequestUrlHost = setupOptions.RequestUrlHost; + shellSettings.RequestUrlPrefix = setupOptions.RequestUrlPrefix; + + shellSettings["ConnectionString"] = setupOptions.DatabaseConnectionString; + shellSettings["TablePrefix"] = setupOptions.DatabaseTablePrefix; + shellSettings["Schema"] = setupOptions.DatabaseSchema; + shellSettings["DatabaseProvider"] = setupOptions.DatabaseProvider; + shellSettings["Secret"] = Guid.NewGuid().ToString(); + shellSettings["RecipeName"] = setupOptions.RecipeName; + shellSettings["FeatureProfile"] = setupOptions.FeatureProfile; + + await _shellHost.UpdateShellSettingsAsync(shellSettings); + + return shellSettings; + } - if (shellSettings.IsDefaultShell()) - { - // The 'Default' shell is first created by the infrastructure, - // so the following 'Autosetup' options need to be passed. - shellSettings.RequestUrlHost = options.RequestUrlHost; - shellSettings.RequestUrlPrefix = options.RequestUrlPrefix; - } + /// + /// Gets a setup context from the configuration. + /// + /// The tenant setup options. + /// The setup service. + /// The tenant shell settings. + /// The used to setup the site. + private static async Task GetSetupContextAsync(TenantSetupOptions options, ISetupService setupService, ShellSettings shellSettings) + { + var recipes = await setupService.GetSetupRecipesAsync(); + + var recipe = recipes.SingleOrDefault(r => r.Name == options.RecipeName); - setupContext.Properties[SetupConstants.AdminEmail] = options.AdminEmail; - setupContext.Properties[SetupConstants.AdminPassword] = options.AdminPassword; - setupContext.Properties[SetupConstants.AdminUsername] = options.AdminUsername; - setupContext.Properties[SetupConstants.DatabaseConnectionString] = options.DatabaseConnectionString; - setupContext.Properties[SetupConstants.DatabaseProvider] = options.DatabaseProvider; - setupContext.Properties[SetupConstants.DatabaseTablePrefix] = options.DatabaseTablePrefix; - setupContext.Properties[SetupConstants.DatabaseSchema] = options.DatabaseSchema; - setupContext.Properties[SetupConstants.SiteName] = options.SiteName; - setupContext.Properties[SetupConstants.SiteTimeZone] = options.SiteTimeZone; - - return setupContext; + var setupContext = new SetupContext + { + Recipe = recipe, + ShellSettings = shellSettings, + Errors = new Dictionary() + }; + + if (shellSettings.IsDefaultShell()) + { + // The 'Default' shell is first created by the infrastructure, + // so the following 'Autosetup' options need to be passed. + shellSettings.RequestUrlHost = options.RequestUrlHost; + shellSettings.RequestUrlPrefix = options.RequestUrlPrefix; } + + setupContext.Properties[SetupConstants.AdminEmail] = options.AdminEmail; + setupContext.Properties[SetupConstants.AdminPassword] = options.AdminPassword; + setupContext.Properties[SetupConstants.AdminUsername] = options.AdminUsername; + setupContext.Properties[SetupConstants.DatabaseConnectionString] = options.DatabaseConnectionString; + setupContext.Properties[SetupConstants.DatabaseProvider] = options.DatabaseProvider; + setupContext.Properties[SetupConstants.DatabaseTablePrefix] = options.DatabaseTablePrefix; + setupContext.Properties[SetupConstants.DatabaseSchema] = options.DatabaseSchema; + setupContext.Properties[SetupConstants.SiteName] = options.SiteName; + setupContext.Properties[SetupConstants.SiteTimeZone] = options.SiteTimeZone; + + return setupContext; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Extensions/DistributedLockExtensions.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Extensions/DistributedLockExtensions.cs index e54f268ddc6..3bbd1459e1b 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Extensions/DistributedLockExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Extensions/DistributedLockExtensions.cs @@ -4,53 +4,52 @@ using OrchardCore.Locking; using OrchardCore.Locking.Distributed; -namespace OrchardCore.AutoSetup.Extensions +namespace OrchardCore.AutoSetup.Extensions; + +/// +/// The distributed lock extensions for auto setup. +/// +public static class DistributedLockExtensions { /// - /// The distributed lock extensions for auto setup. + /// Tries to acquire an auto setup lock. /// - public static class DistributedLockExtensions + /// + /// The distributed lock. + /// + /// + /// The auto setup lock options. + /// + /// + /// The and true if successfully acquired. + /// + public static Task<(ILocker locker, bool locked)> TryAcquireAutoSetupLockAsync(this IDistributedLock distributedLock, LockOptions lockOptions) { - /// - /// Tries to acquire an auto setup lock. - /// - /// - /// The distributed lock. - /// - /// - /// The auto setup lock options. - /// - /// - /// The and true if successfully acquired. - /// - public static Task<(ILocker locker, bool locked)> TryAcquireAutoSetupLockAsync(this IDistributedLock distributedLock, LockOptions lockOptions) + TimeSpan timeout, expiration; + if (distributedLock is ILocalLock) + { + // If it is a local lock, don't use any timeout and expiration. + timeout = expiration = TimeSpan.MaxValue; + } + else { - TimeSpan timeout, expiration; - if (distributedLock is ILocalLock) + // If it is a distributed lock, use the configured timeout and expiration. + var lockTimeout = lockOptions?.LockTimeout ?? 0; + if (lockTimeout <= 0) { - // If it is a local lock, don't use any timeout and expiration. - timeout = expiration = TimeSpan.MaxValue; + lockTimeout = 60_000; } - else - { - // If it is a distributed lock, use the configured timeout and expiration. - var lockTimeout = lockOptions?.LockTimeout ?? 0; - if (lockTimeout <= 0) - { - lockTimeout = 60_000; - } - var lockExpiration = lockOptions?.LockExpiration ?? 0; - if (lockExpiration <= 0) - { - lockExpiration = 60_000; - } - - timeout = TimeSpan.FromMilliseconds(lockTimeout); - expiration = TimeSpan.FromMilliseconds(lockExpiration); + var lockExpiration = lockOptions?.LockExpiration ?? 0; + if (lockExpiration <= 0) + { + lockExpiration = 60_000; } - return distributedLock.TryAcquireLockAsync("AUTOSETUP_LOCK", timeout, expiration); + timeout = TimeSpan.FromMilliseconds(lockTimeout); + expiration = TimeSpan.FromMilliseconds(lockExpiration); } + + return distributedLock.TryAcquireLockAsync("AUTOSETUP_LOCK", timeout, expiration); } } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/AutoSetupOptions.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/AutoSetupOptions.cs index 4f648fed661..42c749c4088 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/AutoSetupOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/AutoSetupOptions.cs @@ -3,65 +3,64 @@ using System.ComponentModel.DataAnnotations; using System.Linq; -namespace OrchardCore.AutoSetup.Options +namespace OrchardCore.AutoSetup.Options; + +/// +/// The auto setup options. +/// +public class AutoSetupOptions : IValidatableObject { /// - /// The auto setup options. + /// Gets or sets the url which will trigger the auto setup. + /// Leave it empty if you want to trigger the auto setup on any request. /// - public class AutoSetupOptions : IValidatableObject - { - /// - /// Gets or sets the url which will trigger the auto setup. - /// Leave it empty if you want to trigger the auto setup on any request. - /// - public string AutoSetupPath { get; set; } + public string AutoSetupPath { get; set; } - /// - /// Auto setup lock options. - /// - public LockOptions LockOptions { get; set; } + /// + /// Auto setup lock options. + /// + public LockOptions LockOptions { get; set; } - /// - /// Gets or sets the tenants to install. - /// - public List Tenants { get; set; } = []; + /// + /// Gets or sets the tenants to install. + /// + public List Tenants { get; set; } = []; - /// - /// Whether the configuration section exists. - /// - public bool ConfigurationExists { get; set; } + /// + /// Whether the configuration section exists. + /// + public bool ConfigurationExists { get; set; } - /// - /// Auto setup options validation logic. - /// - /// The validation context. - /// The collection of errors. - public IEnumerable Validate(ValidationContext validationContext) + /// + /// Auto setup options validation logic. + /// + /// The validation context. + /// The collection of errors. + public IEnumerable Validate(ValidationContext validationContext) + { + if (!string.IsNullOrWhiteSpace(AutoSetupPath) && !AutoSetupPath.StartsWith('/')) { - if (!string.IsNullOrWhiteSpace(AutoSetupPath) && !AutoSetupPath.StartsWith('/')) - { - yield return new ValidationResult($"The field '{nameof(AutoSetupPath)}' should be empty or start with '/'."); - } + yield return new ValidationResult($"The field '{nameof(AutoSetupPath)}' should be empty or start with '/'."); + } - if (Tenants.Count == 0) - { - yield return new ValidationResult($"The field '{nameof(Tenants)}' should contain at least one tenant."); - } + if (Tenants.Count == 0) + { + yield return new ValidationResult($"The field '{nameof(Tenants)}' should contain at least one tenant."); + } - if (Tenants.Count(tenant => tenant.IsDefault) != 1) - { - yield return new ValidationResult("The single 'Default' tenant should be provided."); - } + if (Tenants.Count(tenant => tenant.IsDefault) != 1) + { + yield return new ValidationResult("The single 'Default' tenant should be provided."); + } - if (LockOptions != null && (LockOptions.LockExpiration <= 0 || LockOptions.LockTimeout <= 0)) - { - yield return new ValidationResult("Lock option's 'LockExpiration' and 'LockTimeout' should be greater than zero."); - } + if (LockOptions != null && (LockOptions.LockExpiration <= 0 || LockOptions.LockTimeout <= 0)) + { + yield return new ValidationResult("Lock option's 'LockExpiration' and 'LockTimeout' should be greater than zero."); + } - foreach (var validationResult in Tenants.SelectMany(tenant => tenant.Validate(validationContext))) - { - yield return validationResult; - } + foreach (var validationResult in Tenants.SelectMany(tenant => tenant.Validate(validationContext))) + { + yield return validationResult; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/LockOptions.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/LockOptions.cs index dff815f8172..abb776bc02b 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/LockOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/LockOptions.cs @@ -1,18 +1,17 @@ -namespace OrchardCore.AutoSetup.Options +namespace OrchardCore.AutoSetup.Options; + +/// +/// The auto setup lock options. +/// +public class LockOptions { /// - /// The auto setup lock options. + /// The timeout in milliseconds to acquire a distributed setup lock. /// - public class LockOptions - { - /// - /// The timeout in milliseconds to acquire a distributed setup lock. - /// - public int LockTimeout { get; set; } + public int LockTimeout { get; set; } - /// - /// The expiration in milliseconds of the distributed setup lock. - /// - public int LockExpiration { get; set; } - } + /// + /// The expiration in milliseconds of the distributed setup lock. + /// + public int LockExpiration { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/TenantSetupOptions.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/TenantSetupOptions.cs index 13433489b48..2fe9795fb9f 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/TenantSetupOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Options/TenantSetupOptions.cs @@ -6,158 +6,157 @@ using OrchardCore.Data; using OrchardCore.Environment.Shell; -namespace OrchardCore.AutoSetup.Options +namespace OrchardCore.AutoSetup.Options; + +/// +/// The tenant setup options. +/// +public partial class TenantSetupOptions { + private readonly string _requiredErrorMessageFormat = "The {0} field is required."; + + private bool? _isDefault; + + /// + /// The Shell Name. + /// + public string ShellName { get; set; } + + /// + /// Gets or sets the user friendly Tenant/Site Name. + /// + public string SiteName { get; set; } + + /// + /// Gets or sets the admin username. + /// + public string AdminUsername { get; set; } + + /// + /// Gets or sets the admin email. + /// + public string AdminEmail { get; set; } + + /// + /// Gets or sets the admin password. + /// + public string AdminPassword { get; set; } + + /// + /// Gets or sets the database provider. + /// + public string DatabaseProvider { get; set; } + + /// + /// Gets or sets the database connection string. + /// + public string DatabaseConnectionString { get; set; } + + /// + /// Gets or sets the database table prefix. + /// + public string DatabaseTablePrefix { get; set; } + + /// + /// Gets or sets the database's schema. + /// + public string DatabaseSchema { get; set; } + + /// + /// Gets or sets the recipe name. + /// + public string RecipeName { get; set; } + + /// + /// Gets or sets the site time zone. + /// + public string SiteTimeZone { get; set; } + + /// + /// Gets or sets the tenants request url host. + /// + public string RequestUrlHost { get; set; } + /// - /// The tenant setup options. + /// Gets or sets the tenant request url prefix. /// - public partial class TenantSetupOptions + public string RequestUrlPrefix { get; set; } + + /// + /// Gets or sets the name of the feature profile applied to the tenant. May be or empty + /// for no profile being selected. + /// + public string FeatureProfile { get; set; } + + /// + /// Gets the Flag which indicates a Default/Root shell/tenant. + /// + public bool IsDefault => _isDefault ??= ShellName.IsDefaultShellName(); + + /// + /// Tenant validation. + /// + /// The validation context. + /// The collection of errors. + public virtual IEnumerable Validate(ValidationContext validationContext) { - private readonly string _requiredErrorMessageFormat = "The {0} field is required."; - - private bool? _isDefault; - - /// - /// The Shell Name. - /// - public string ShellName { get; set; } - - /// - /// Gets or sets the user friendly Tenant/Site Name. - /// - public string SiteName { get; set; } - - /// - /// Gets or sets the admin username. - /// - public string AdminUsername { get; set; } - - /// - /// Gets or sets the admin email. - /// - public string AdminEmail { get; set; } - - /// - /// Gets or sets the admin password. - /// - public string AdminPassword { get; set; } - - /// - /// Gets or sets the database provider. - /// - public string DatabaseProvider { get; set; } - - /// - /// Gets or sets the database connection string. - /// - public string DatabaseConnectionString { get; set; } - - /// - /// Gets or sets the database table prefix. - /// - public string DatabaseTablePrefix { get; set; } - - /// - /// Gets or sets the database's schema. - /// - public string DatabaseSchema { get; set; } - - /// - /// Gets or sets the recipe name. - /// - public string RecipeName { get; set; } - - /// - /// Gets or sets the site time zone. - /// - public string SiteTimeZone { get; set; } - - /// - /// Gets or sets the tenants request url host. - /// - public string RequestUrlHost { get; set; } - - /// - /// Gets or sets the tenant request url prefix. - /// - public string RequestUrlPrefix { get; set; } - - /// - /// Gets or sets the name of the feature profile applied to the tenant. May be or empty - /// for no profile being selected. - /// - public string FeatureProfile { get; set; } - - /// - /// Gets the Flag which indicates a Default/Root shell/tenant. - /// - public bool IsDefault => _isDefault ??= ShellName.IsDefaultShellName(); - - /// - /// Tenant validation. - /// - /// The validation context. - /// The collection of errors. - public virtual IEnumerable Validate(ValidationContext validationContext) + if (string.IsNullOrEmpty(ShellName) || !TenantNameRegex().IsMatch(ShellName)) + { + yield return new ValidationResult("ShellName Can not be empty and must contain characters only and no spaces."); + } + + if (!IsDefault && ShellName.IsDefaultShellNameIgnoreCase()) + { + yield return new ValidationResult("The tenant name is in conflict with the 'Default' tenant name."); + } + + if (!string.IsNullOrWhiteSpace(RequestUrlPrefix) && RequestUrlPrefix.Contains('/')) { - if (string.IsNullOrEmpty(ShellName) || !TenantNameRegex().IsMatch(ShellName)) - { - yield return new ValidationResult("ShellName Can not be empty and must contain characters only and no spaces."); - } - - if (!IsDefault && ShellName.IsDefaultShellNameIgnoreCase()) - { - yield return new ValidationResult("The tenant name is in conflict with the 'Default' tenant name."); - } - - if (!string.IsNullOrWhiteSpace(RequestUrlPrefix) && RequestUrlPrefix.Contains('/')) - { - yield return new ValidationResult("The RequestUrlPrefix can not contain more than one segment."); - } - - if (string.IsNullOrWhiteSpace(SiteName)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(SiteName))); - } - - if (string.IsNullOrWhiteSpace(AdminUsername)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminUsername))); - } - - if (string.IsNullOrWhiteSpace(AdminEmail)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminEmail))); - } - - if (string.IsNullOrWhiteSpace(AdminPassword)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminPassword))); - } - - var selectedProvider = validationContext.GetServices().FirstOrDefault(x => x.Value == DatabaseProvider); - if (selectedProvider == null) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(DatabaseProvider))); - } - - if (selectedProvider != null && selectedProvider.HasConnectionString && string.IsNullOrWhiteSpace(DatabaseConnectionString)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(DatabaseConnectionString))); - } - - if (string.IsNullOrWhiteSpace(RecipeName)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(RecipeName))); - } - - if (string.IsNullOrWhiteSpace(SiteTimeZone)) - { - yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(SiteTimeZone))); - } + yield return new ValidationResult("The RequestUrlPrefix can not contain more than one segment."); } - [GeneratedRegex(@"^\w+$")] - private static partial Regex TenantNameRegex(); + if (string.IsNullOrWhiteSpace(SiteName)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(SiteName))); + } + + if (string.IsNullOrWhiteSpace(AdminUsername)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminUsername))); + } + + if (string.IsNullOrWhiteSpace(AdminEmail)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminEmail))); + } + + if (string.IsNullOrWhiteSpace(AdminPassword)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(AdminPassword))); + } + + var selectedProvider = validationContext.GetServices().FirstOrDefault(x => x.Value == DatabaseProvider); + if (selectedProvider == null) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(DatabaseProvider))); + } + + if (selectedProvider != null && selectedProvider.HasConnectionString && string.IsNullOrWhiteSpace(DatabaseConnectionString)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(DatabaseConnectionString))); + } + + if (string.IsNullOrWhiteSpace(RecipeName)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(RecipeName))); + } + + if (string.IsNullOrWhiteSpace(SiteTimeZone)) + { + yield return new ValidationResult(string.Format(_requiredErrorMessageFormat, nameof(SiteTimeZone))); + } } + + [GeneratedRegex(@"^\w+$")] + private static partial Regex TenantNameRegex(); } diff --git a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Startup.cs b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Startup.cs index 5ab8fd246ae..022c01aa76e 100644 --- a/src/OrchardCore.Modules/OrchardCore.AutoSetup/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.AutoSetup/Startup.cs @@ -14,107 +14,106 @@ using OrchardCore.Modules; using OrchardCore.Routing; -namespace OrchardCore.AutoSetup +namespace OrchardCore.AutoSetup; + +/// +/// The AutoSetup feature startup. +/// +public sealed class Startup : StartupBase { /// - /// The AutoSetup feature startup. + /// AutoSetup Configuration Section Name. /// - public sealed class Startup : StartupBase - { - /// - /// AutoSetup Configuration Section Name. - /// - private const string ConfigSectionName = "OrchardCore_AutoSetup"; + private const string ConfigSectionName = "OrchardCore_AutoSetup"; - /// - /// The Shell settings. - /// - private readonly ShellSettings _shellSettings; + /// + /// The Shell settings. + /// + private readonly ShellSettings _shellSettings; - /// - /// The Shell/Tenant configuration. - /// - private readonly IShellConfiguration _shellConfiguration; + /// + /// The Shell/Tenant configuration. + /// + private readonly IShellConfiguration _shellConfiguration; - /// - /// The logger. - /// - private readonly ILogger _logger; + /// + /// The logger. + /// + private readonly ILogger _logger; - /// - /// Initializes a new instance of the class. - /// - /// The Shell settings. - /// The shell configuration. - /// The logger. - public Startup(ShellSettings shellSettings, IShellConfiguration shellConfiguration, ILogger logger) + /// + /// Initializes a new instance of the class. + /// + /// The Shell settings. + /// The shell configuration. + /// The logger. + public Startup(ShellSettings shellSettings, IShellConfiguration shellConfiguration, ILogger logger) + { + _shellSettings = shellSettings; + _shellConfiguration = shellConfiguration; + _logger = logger; + } + + /// + /// The configure services. + /// + /// + /// The services. + /// + public override void ConfigureServices(IServiceCollection services) + { + var configuration = _shellConfiguration.GetSection(ConfigSectionName); + services.Configure(configuration); + + if (configuration.Exists()) { - _shellSettings = shellSettings; - _shellConfiguration = shellConfiguration; - _logger = logger; + services.Configure(o => o.ConfigurationExists = true); } + } - /// - /// The configure services. - /// - /// - /// The services. - /// - public override void ConfigureServices(IServiceCollection services) + /// + /// Configure application pipeline. + /// + /// + /// The app. + /// + /// + /// The routes. + /// + /// + /// The "Shell Scope" service provider. + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + if (_shellSettings.IsUninitialized()) { - var configuration = _shellConfiguration.GetSection(ConfigSectionName); - services.Configure(configuration); - - if (configuration.Exists()) + var options = serviceProvider.GetRequiredService>().Value; + if (!options.ConfigurationExists) { - services.Configure(o => o.ConfigurationExists = true); + return; } - } - /// - /// Configure application pipeline. - /// - /// - /// The app. - /// - /// - /// The routes. - /// - /// - /// The "Shell Scope" service provider. - /// - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - if (_shellSettings.IsUninitialized()) + var validationContext = new ValidationContext(options, serviceProvider, null); + + var validationErrors = options.Validate(validationContext); + if (validationErrors.Any()) { - var options = serviceProvider.GetRequiredService>().Value; - if (!options.ConfigurationExists) + var stringBuilder = new StringBuilder(); + foreach (var error in validationErrors) { - return; + stringBuilder.Append(error.ErrorMessage + ' '); } - var validationContext = new ValidationContext(options, serviceProvider, null); - - var validationErrors = options.Validate(validationContext); - if (validationErrors.Any()) - { - var stringBuilder = new StringBuilder(); - foreach (var error in validationErrors) - { - stringBuilder.Append(error.ErrorMessage + ' '); - } - - _logger.LogError("AutoSetup did not start, configuration has following errors: {errors}", stringBuilder.ToString()); - } - else if (string.IsNullOrWhiteSpace(options.AutoSetupPath)) - { - app.UseMiddleware(); - } - else - { - app.MapWhen(ctx => ctx.Request.Path.StartsWithNormalizedSegments(options.AutoSetupPath), - appBuilder => appBuilder.UseMiddleware()); - } + _logger.LogError("AutoSetup did not start, configuration has following errors: {errors}", stringBuilder.ToString()); + } + else if (string.IsNullOrWhiteSpace(options.AutoSetupPath)) + { + app.UseMiddleware(); + } + else + { + app.MapWhen(ctx => ctx.Request.Path.StartsWithNormalizedSegments(options.AutoSetupPath), + appBuilder => appBuilder.UseMiddleware()); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Drivers/AutoroutePartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Drivers/AutoroutePartDisplayDriver.cs index a3bb9bfb137..1594a91b707 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Drivers/AutoroutePartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Drivers/AutoroutePartDisplayDriver.cs @@ -18,122 +18,121 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Autoroute.Drivers +namespace OrchardCore.Autoroute.Drivers; + +public sealed class AutoroutePartDisplayDriver : ContentPartDisplayDriver { - public sealed class AutoroutePartDisplayDriver : ContentPartDisplayDriver + private readonly AutorouteOptions _options; + private readonly ISiteService _siteService; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly YesSql.ISession _session; + + internal readonly IStringLocalizer S; + + public AutoroutePartDisplayDriver( + IOptions options, + ISiteService siteService, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor, + YesSql.ISession session, + IStringLocalizer localizer + ) { - private readonly AutorouteOptions _options; - private readonly ISiteService _siteService; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly YesSql.ISession _session; - - internal readonly IStringLocalizer S; - - public AutoroutePartDisplayDriver( - IOptions options, - ISiteService siteService, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor, - YesSql.ISession session, - IStringLocalizer localizer - ) - { - _options = options.Value; - _siteService = siteService; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - _session = session; - S = localizer; - } + _options = options.Value; + _siteService = siteService; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + _session = session; + S = localizer; + } - public override IDisplayResult Edit(AutoroutePart autoroutePart, BuildPartEditorContext context) + public override IDisplayResult Edit(AutoroutePart autoroutePart, BuildPartEditorContext context) + { + return Initialize("AutoroutePart_Edit", async model => { - return Initialize("AutoroutePart_Edit", async model => - { - model.Path = autoroutePart.Path; - model.AutoroutePart = autoroutePart; - model.ContentItem = autoroutePart.ContentItem; - model.SetHomepage = false; + model.Path = autoroutePart.Path; + model.AutoroutePart = autoroutePart; + model.ContentItem = autoroutePart.ContentItem; + model.SetHomepage = false; - var siteSettings = await _siteService.GetSiteSettingsAsync(); - var homeRoute = siteSettings.HomeRoute; + var siteSettings = await _siteService.GetSiteSettingsAsync(); + var homeRoute = siteSettings.HomeRoute; - if (homeRoute != null && homeRoute.TryGetValue(_options.ContainedContentItemIdKey, out var containedContentItemId)) - { - if (string.Equals(autoroutePart.ContentItem.ContentItemId, containedContentItemId.ToString(), StringComparison.OrdinalIgnoreCase)) - { - model.IsHomepage = true; - } - } - else if (string.Equals(autoroutePart.ContentItem.ContentItemId, homeRoute?[_options.ContentItemIdKey]?.ToString(), StringComparison.OrdinalIgnoreCase)) + if (homeRoute != null && homeRoute.TryGetValue(_options.ContainedContentItemIdKey, out var containedContentItemId)) + { + if (string.Equals(autoroutePart.ContentItem.ContentItemId, containedContentItemId.ToString(), StringComparison.OrdinalIgnoreCase)) { model.IsHomepage = true; } + } + else if (string.Equals(autoroutePart.ContentItem.ContentItemId, homeRoute?[_options.ContentItemIdKey]?.ToString(), StringComparison.OrdinalIgnoreCase)) + { + model.IsHomepage = true; + } - model.Disabled = autoroutePart.Disabled; - model.Absolute = autoroutePart.Absolute; - model.RouteContainedItems = autoroutePart.RouteContainedItems; + model.Disabled = autoroutePart.Disabled; + model.Absolute = autoroutePart.Absolute; + model.RouteContainedItems = autoroutePart.RouteContainedItems; - model.Settings = context.TypePartDefinition.GetSettings(); - }); - } + model.Settings = context.TypePartDefinition.GetSettings(); + }); + } - public override async Task UpdateAsync(AutoroutePart model, UpdatePartEditorContext context) - { - var viewModel = new AutoroutePartViewModel(); + public override async Task UpdateAsync(AutoroutePart model, UpdatePartEditorContext context) + { + var viewModel = new AutoroutePartViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, - t => t.Path, - t => t.UpdatePath, - t => t.RouteContainedItems, - t => t.Absolute, - t => t.Disabled); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, + t => t.Path, + t => t.UpdatePath, + t => t.RouteContainedItems, + t => t.Absolute, + t => t.Disabled); - var settings = context.TypePartDefinition.GetSettings(); + var settings = context.TypePartDefinition.GetSettings(); - model.Disabled = viewModel.Disabled; - model.Absolute = viewModel.Absolute; - model.RouteContainedItems = viewModel.RouteContainedItems; + model.Disabled = viewModel.Disabled; + model.Absolute = viewModel.Absolute; + model.RouteContainedItems = viewModel.RouteContainedItems; - // When disabled these values are not updated. - if (!model.Disabled) + // When disabled these values are not updated. + if (!model.Disabled) + { + if (settings.AllowCustomPath) { - if (settings.AllowCustomPath) - { - model.Path = viewModel.Path; - } + model.Path = viewModel.Path; + } - if (settings.AllowUpdatePath && viewModel.UpdatePath) - { - // Make it empty to force a regeneration - model.Path = string.Empty; - } + if (settings.AllowUpdatePath && viewModel.UpdatePath) + { + // Make it empty to force a regeneration + model.Path = string.Empty; + } - var httpContext = _httpContextAccessor.HttpContext; + var httpContext = _httpContextAccessor.HttpContext; - if (httpContext != null && await _authorizationService.AuthorizeAsync(httpContext.User, Permissions.SetHomepage)) - { - await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.SetHomepage); - } + if (httpContext != null && await _authorizationService.AuthorizeAsync(httpContext.User, Permissions.SetHomepage)) + { + await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.SetHomepage); + } - context.Updater.ModelState.BindValidationResults(Prefix, model.ValidatePathFieldValue(S)); + context.Updater.ModelState.BindValidationResults(Prefix, model.ValidatePathFieldValue(S)); - // This can only validate the path if the Autoroute is not managing content item routes or the path is absolute. - if (!string.IsNullOrEmpty(model.Path) && (!settings.ManageContainedItemRoutes || (settings.ManageContainedItemRoutes && model.Absolute))) + // This can only validate the path if the Autoroute is not managing content item routes or the path is absolute. + if (!string.IsNullOrEmpty(model.Path) && (!settings.ManageContainedItemRoutes || (settings.ManageContainedItemRoutes && model.Absolute))) + { + var path = model.Path.Trim('/'); + var paths = new string[] { path, "/" + path, path + "/", "/" + path + "/" }; + + var possibleConflicts = await _session.QueryIndex(o => (o.Published || o.Latest) && o.Path.IsIn(paths)).ListAsync(); + if (possibleConflicts.Any(x => x.ContentItemId != model.ContentItem.ContentItemId && x.ContainedContentItemId != model.ContentItem.ContentItemId)) { - var path = model.Path.Trim('/'); - var paths = new string[] { path, "/" + path, path + "/", "/" + path + "/" }; - - var possibleConflicts = await _session.QueryIndex(o => (o.Published || o.Latest) && o.Path.IsIn(paths)).ListAsync(); - if (possibleConflicts.Any(x => x.ContentItemId != model.ContentItem.ContentItemId && x.ContainedContentItemId != model.ContentItem.ContentItemId)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Path), S["Your permalink is already in use."]); - } + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Path), S["Your permalink is already in use."]); } } - - return Edit(model, context); } + + return Edit(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs index 7cece8ddf4f..bf996ee1e0b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteInputObjectType.cs @@ -3,16 +3,15 @@ using OrchardCore.Apis.GraphQL.Queries; using OrchardCore.Autoroute.Models; -namespace OrchardCore.Autoroute.GraphQL +namespace OrchardCore.Autoroute.GraphQL; + +public class AutorouteInputObjectType : WhereInputObjectGraphType { - public class AutorouteInputObjectType : WhereInputObjectGraphType + public AutorouteInputObjectType(IStringLocalizer S) { - public AutorouteInputObjectType(IStringLocalizer S) - { - Name = "AutoroutePartInput"; - Description = S["the custom URL part of the content item"]; + Name = "AutoroutePartInput"; + Description = S["the custom URL part of the content item"]; - AddScalarFilterFields("path", S["the path of the content item to filter"]); - } + AddScalarFilterFields("path", S["the path of the content item to filter"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutoroutePartIndexAliasProvider.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutoroutePartIndexAliasProvider.cs index 81b6407c6bf..dada9f1c5c7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutoroutePartIndexAliasProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutoroutePartIndexAliasProvider.cs @@ -3,23 +3,22 @@ using OrchardCore.Autoroute.Core.Indexes; using OrchardCore.ContentManagement.GraphQL.Queries; -namespace OrchardCore.Autoroute.GraphQL -{ - public class AutoroutePartIndexAliasProvider : IIndexAliasProvider - { - private static readonly IndexAlias[] _aliases = - [ - new IndexAlias - { - Alias = "autoroutePart", - Index = nameof(AutoroutePartIndex), - IndexType = typeof(AutoroutePartIndex) - } - ]; +namespace OrchardCore.Autoroute.GraphQL; - public ValueTask> GetAliasesAsync() +public class AutoroutePartIndexAliasProvider : IIndexAliasProvider +{ + private static readonly IndexAlias[] _aliases = + [ + new IndexAlias { - return ValueTask.FromResult>(_aliases); + Alias = "autoroutePart", + Index = nameof(AutoroutePartIndex), + IndexType = typeof(AutoroutePartIndex) } + ]; + + public ValueTask> GetAliasesAsync() + { + return ValueTask.FromResult>(_aliases); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteQueryObjectType.cs index 064d47ad787..36c681a277d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/AutorouteQueryObjectType.cs @@ -2,16 +2,15 @@ using Microsoft.Extensions.Localization; using OrchardCore.Autoroute.Models; -namespace OrchardCore.Autoroute.GraphQL +namespace OrchardCore.Autoroute.GraphQL; + +public class AutorouteQueryObjectType : ObjectGraphType { - public class AutorouteQueryObjectType : ObjectGraphType + public AutorouteQueryObjectType(IStringLocalizer S) { - public AutorouteQueryObjectType(IStringLocalizer S) - { - Name = "AutoroutePart"; - Description = S["Custom URLs (permalinks) for your content item."]; + Name = "AutoroutePart"; + Description = S["Custom URLs (permalinks) for your content item."]; - Field(x => x.Path).Description(S["The permalinks for your content item."]); - } + Field(x => x.Path).Description(S["The permalinks for your content item."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/Startup.cs index 5168f9c1210..dda9c0a6e70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/GraphQL/Startup.cs @@ -6,17 +6,16 @@ using OrchardCore.ContentManagement.GraphQL.Queries; using OrchardCore.Modules; -namespace OrchardCore.Autoroute.GraphQL +namespace OrchardCore.Autoroute.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddInputObjectGraphType(); - services.AddObjectGraphType(); - services.AddTransient(); - services.AddWhereInputIndexPropertyProvider(); - } + services.AddInputObjectGraphType(); + services.AddObjectGraphType(); + services.AddTransient(); + services.AddWhereInputIndexPropertyProvider(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutorouteContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutorouteContentHandler.cs index 73285044ae0..e14404846b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutorouteContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutorouteContentHandler.cs @@ -4,35 +4,34 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.ContentManagement.Routing; -namespace OrchardCore.Autoroute.Handlers +namespace OrchardCore.Autoroute.Handlers; + +public class AutorouteContentHandler : ContentHandlerBase { - public class AutorouteContentHandler : ContentHandlerBase + private readonly IAutorouteEntries _autorouteEntries; + + public AutorouteContentHandler(IAutorouteEntries autorouteEntries) { - private readonly IAutorouteEntries _autorouteEntries; + _autorouteEntries = autorouteEntries; + } - public AutorouteContentHandler(IAutorouteEntries autorouteEntries) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + { + return context.ForAsync(async metadata => { - _autorouteEntries = autorouteEntries; - } + // When a content item is contained we provide different route values when generating urls. + (var found, var entry) = await _autorouteEntries.TryGetEntryByContentItemIdAsync(context.ContentItem.ContentItemId); - public override Task GetContentItemAspectAsync(ContentItemAspectContext context) - { - return context.ForAsync(async metadata => + if (found && !string.IsNullOrEmpty(entry.ContainedContentItemId)) { - // When a content item is contained we provide different route values when generating urls. - (var found, var entry) = await _autorouteEntries.TryGetEntryByContentItemIdAsync(context.ContentItem.ContentItemId); - - if (found && !string.IsNullOrEmpty(entry.ContainedContentItemId)) - { - metadata.DisplayRouteValues = new RouteValueDictionary { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Item" }, - { "Action", "Display" }, - { "ContentItemId", entry.ContentItemId}, - { "ContainedContentItemId", entry.ContainedContentItemId } - }; - } - }); - } + metadata.DisplayRouteValues = new RouteValueDictionary { + { "Area", "OrchardCore.Contents" }, + { "Controller", "Item" }, + { "Action", "Display" }, + { "ContentItemId", entry.ContentItemId}, + { "ContainedContentItemId", entry.ContainedContentItemId } + }; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutoroutePartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutoroutePartHandler.cs index 554b5f8b654..7be8c220c10 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutoroutePartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/AutoroutePartHandler.cs @@ -25,277 +25,295 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Autoroute.Handlers +namespace OrchardCore.Autoroute.Handlers; + +public class AutoroutePartHandler : ContentPartHandler { - public class AutoroutePartHandler : ContentPartHandler + private readonly IAutorouteEntries _entries; + private readonly AutorouteOptions _options; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ISiteService _siteService; + private readonly ITagCache _tagCache; + private readonly ISession _session; + private readonly IServiceProvider _serviceProvider; + + protected readonly IStringLocalizer S; + + private IContentManager _contentManager; + + public AutoroutePartHandler( + IAutorouteEntries entries, + IOptions options, + ILiquidTemplateManager liquidTemplateManager, + IContentDefinitionManager contentDefinitionManager, + ISiteService siteService, + ITagCache tagCache, + ISession session, + IServiceProvider serviceProvider, + IStringLocalizer stringLocalizer) { - private readonly IAutorouteEntries _entries; - private readonly AutorouteOptions _options; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ISiteService _siteService; - private readonly ITagCache _tagCache; - private readonly ISession _session; - private readonly IServiceProvider _serviceProvider; - - protected readonly IStringLocalizer S; - - private IContentManager _contentManager; - - public AutoroutePartHandler( - IAutorouteEntries entries, - IOptions options, - ILiquidTemplateManager liquidTemplateManager, - IContentDefinitionManager contentDefinitionManager, - ISiteService siteService, - ITagCache tagCache, - ISession session, - IServiceProvider serviceProvider, - IStringLocalizer stringLocalizer) - { - _entries = entries; - _options = options.Value; - _liquidTemplateManager = liquidTemplateManager; - _contentDefinitionManager = contentDefinitionManager; - _siteService = siteService; - _tagCache = tagCache; - _session = session; - _serviceProvider = serviceProvider; - S = stringLocalizer; - } + _entries = entries; + _options = options.Value; + _liquidTemplateManager = liquidTemplateManager; + _contentDefinitionManager = contentDefinitionManager; + _siteService = siteService; + _tagCache = tagCache; + _session = session; + _serviceProvider = serviceProvider; + S = stringLocalizer; + } - public override async Task PublishedAsync(PublishContentContext context, AutoroutePart part) + public override async Task PublishedAsync(PublishContentContext context, AutoroutePart part) + { + if (!string.IsNullOrWhiteSpace(part.Path)) { - if (!string.IsNullOrWhiteSpace(part.Path)) - { - if (part.RouteContainedItems) - { - _contentManager ??= _serviceProvider.GetRequiredService(); - var containedAspect = await _contentManager.PopulateAspectAsync(context.PublishingItem); - await CheckContainedHomeRouteAsync(part.ContentItem.ContentItemId, containedAspect, (JsonObject)context.PublishingItem.Content); - } - - // Update entries from the index table after the session is committed. - await _entries.UpdateEntriesAsync(); - } - - if (!string.IsNullOrWhiteSpace(part.Path) && !part.Disabled && part.SetHomepage) + if (part.RouteContainedItems) { - await SetHomeRouteAsync(part, homeRoute => - { - homeRoute[_options.ContentItemIdKey] = context.ContentItem.ContentItemId; - homeRoute.Remove(_options.JsonPathKey); - }); + _contentManager ??= _serviceProvider.GetRequiredService(); + var containedAspect = await _contentManager.PopulateAspectAsync(context.PublishingItem); + await CheckContainedHomeRouteAsync(part.ContentItem.ContentItemId, containedAspect, (JsonObject)context.PublishingItem.Content); } - // Evict any dependent item from cache. - await RemoveTagAsync(part); + // Update entries from the index table after the session is committed. + await _entries.UpdateEntriesAsync(); } - public override async Task UnpublishedAsync(PublishContentContext context, AutoroutePart part) + if (!string.IsNullOrWhiteSpace(part.Path) && !part.Disabled && part.SetHomepage) { - if (!string.IsNullOrWhiteSpace(part.Path)) + await SetHomeRouteAsync(part, homeRoute => { - // Update entries from the index table after the session is committed. - await _entries.UpdateEntriesAsync(); - - // Evict any dependent item from cache. - await RemoveTagAsync(part); - } + homeRoute[_options.ContentItemIdKey] = context.ContentItem.ContentItemId; + homeRoute.Remove(_options.JsonPathKey); + }); } - public override async Task RemovedAsync(RemoveContentContext context, AutoroutePart part) + // Evict any dependent item from cache. + await RemoveTagAsync(part); + } + + public override async Task UnpublishedAsync(PublishContentContext context, AutoroutePart part) + { + if (!string.IsNullOrWhiteSpace(part.Path)) { - if (!string.IsNullOrWhiteSpace(part.Path) && context.NoActiveVersionLeft) - { - // Update entries from the index table after the session is committed. - await _entries.UpdateEntriesAsync(); + // Update entries from the index table after the session is committed. + await _entries.UpdateEntriesAsync(); - // Evict any dependent item from cache. - await RemoveTagAsync(part); - } + // Evict any dependent item from cache. + await RemoveTagAsync(part); } + } - public override async Task ValidatingAsync(ValidateContentContext context, AutoroutePart part) + public override async Task RemovedAsync(RemoveContentContext context, AutoroutePart part) + { + if (!string.IsNullOrWhiteSpace(part.Path) && context.NoActiveVersionLeft) { - // Only validate the path if it's not empty. - if (string.IsNullOrWhiteSpace(part.Path)) - { - return; - } - - foreach (var item in part.ValidatePathFieldValue(S)) - { - context.Fail(item); - } - - if (!await IsAbsolutePathUniqueAsync(part.Path, part.ContentItem.ContentItemId)) - { - context.Fail(S["Your permalink is already in use."], nameof(part.Path)); - } + // Update entries from the index table after the session is committed. + await _entries.UpdateEntriesAsync(); + // Evict any dependent item from cache. + await RemoveTagAsync(part); } + } - public override async Task UpdatedAsync(UpdateContentContext context, AutoroutePart part) + public override async Task ValidatingAsync(ValidateContentContext context, AutoroutePart part) + { + // Only validate the path if it's not empty. + if (string.IsNullOrWhiteSpace(part.Path)) { - await GenerateContainerPathFromPatternAsync(part); - await GenerateContainedPathsFromPatternAsync(context.UpdatingItem, part); + return; } - public async override Task CloningAsync(CloneContentContext context, AutoroutePart part) + foreach (var item in part.ValidatePathFieldValue(S)) { - var clonedPart = context.CloneContentItem.As(); - clonedPart.Path = await GenerateUniqueAbsolutePathAsync(part.Path, context.CloneContentItem.ContentItemId); - clonedPart.SetHomepage = false; - clonedPart.Apply(); - - await GenerateContainedPathsFromPatternAsync(context.CloneContentItem, part); + context.Fail(item); } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, AutoroutePart part) + if (!await IsAbsolutePathUniqueAsync(part.Path, part.ContentItem.ContentItemId)) { - return context.ForAsync(async aspect => - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "AutoroutePart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); - if (settings.ManageContainedItemRoutes) - { - aspect.Path = part.Path; - aspect.Absolute = part.Absolute; - aspect.Disabled = part.Disabled; - } - }); + context.Fail(S["Your permalink is already in use."], nameof(part.Path)); } - private async Task SetHomeRouteAsync(AutoroutePart part, Action action) - { - var site = await _siteService.LoadSiteSettingsAsync(); + } - site.HomeRoute ??= []; + public override async Task UpdatedAsync(UpdateContentContext context, AutoroutePart part) + { + await GenerateContainerPathFromPatternAsync(part); + await GenerateContainedPathsFromPatternAsync(context.UpdatingItem, part); + } + + public async override Task CloningAsync(CloneContentContext context, AutoroutePart part) + { + var clonedPart = context.CloneContentItem.As(); + clonedPart.Path = await GenerateUniqueAbsolutePathAsync(part.Path, context.CloneContentItem.ContentItemId); + clonedPart.SetHomepage = false; + clonedPart.Apply(); - var homeRoute = site.HomeRoute; + await GenerateContainedPathsFromPatternAsync(context.CloneContentItem, part); + } - foreach (var entry in _options.GlobalRouteValues) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, AutoroutePart part) + { + return context.ForAsync(async aspect => + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "AutoroutePart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); + if (settings.ManageContainedItemRoutes) { - homeRoute[entry.Key] = entry.Value; + aspect.Path = part.Path; + aspect.Absolute = part.Absolute; + aspect.Disabled = part.Disabled; } + }); + } - action.Invoke(homeRoute); + private async Task SetHomeRouteAsync(AutoroutePart part, Action action) + { + var site = await _siteService.LoadSiteSettingsAsync(); - // Once we took the flag into account we can dismiss it. - part.SetHomepage = false; - part.Apply(); + site.HomeRoute ??= []; - await _siteService.UpdateSiteSettingsAsync(site); - } + var homeRoute = site.HomeRoute; - private Task RemoveTagAsync(AutoroutePart part) + foreach (var entry in _options.GlobalRouteValues) { - return _tagCache.RemoveTagAsync($"slug:{part.Path}"); + homeRoute[entry.Key] = entry.Value; } - private async Task CheckContainedHomeRouteAsync(string containerContentItemId, ContainedContentItemsAspect containedAspect, JsonObject content) + action.Invoke(homeRoute); + + // Once we took the flag into account we can dismiss it. + part.SetHomepage = false; + part.Apply(); + + await _siteService.UpdateSiteSettingsAsync(site); + } + + private Task RemoveTagAsync(AutoroutePart part) + { + return _tagCache.RemoveTagAsync($"slug:{part.Path}"); + } + + private async Task CheckContainedHomeRouteAsync(string containerContentItemId, ContainedContentItemsAspect containedAspect, JsonObject content) + { + foreach (var accessor in containedAspect.Accessors) { - foreach (var accessor in containedAspect.Accessors) + var jItems = accessor.Invoke(content); + + foreach (var jItem in jItems.Cast()) { - var jItems = accessor.Invoke(content); + var contentItem = jItem.ToObject(); + var handlerAspect = await _contentManager.PopulateAspectAsync(contentItem); - foreach (var jItem in jItems.Cast()) + if (!handlerAspect.Disabled) { - var contentItem = jItem.ToObject(); - var handlerAspect = await _contentManager.PopulateAspectAsync(contentItem); - - if (!handlerAspect.Disabled) + // Only an autoroute part, not a default handler aspect can set itself as the homepage. + var autoroutePart = contentItem.As(); + if (autoroutePart != null && autoroutePart.SetHomepage) { - // Only an autoroute part, not a default handler aspect can set itself as the homepage. - var autoroutePart = contentItem.As(); - if (autoroutePart != null && autoroutePart.SetHomepage) + await SetHomeRouteAsync(autoroutePart, homeRoute => { - await SetHomeRouteAsync(autoroutePart, homeRoute => - { - homeRoute[_options.ContentItemIdKey] = containerContentItemId; - homeRoute[_options.JsonPathKey] = jItem.GetNormalizedPath(); - }); + homeRoute[_options.ContentItemIdKey] = containerContentItemId; + homeRoute[_options.JsonPathKey] = jItem.GetNormalizedPath(); + }); - break; - } + break; } } } } + } - private async Task GenerateContainedPathsFromPatternAsync(ContentItem contentItem, AutoroutePart part) + private async Task GenerateContainedPathsFromPatternAsync(ContentItem contentItem, AutoroutePart part) + { + // Validate contained content item routes if container has valid path. + if (string.IsNullOrWhiteSpace(part.Path) || !part.RouteContainedItems) { - // Validate contained content item routes if container has valid path. - if (string.IsNullOrWhiteSpace(part.Path) || !part.RouteContainedItems) - { - return; - } + return; + } - _contentManager ??= _serviceProvider.GetRequiredService(); - var containedAspect = await _contentManager.PopulateAspectAsync(contentItem); + _contentManager ??= _serviceProvider.GetRequiredService(); + var containedAspect = await _contentManager.PopulateAspectAsync(contentItem); - // Build the entries for this content item to evaluate for duplicates. - var entries = new List(); - await PopulateContainedContentItemRoutesAsync(entries, part.ContentItem.ContentItemId, containedAspect, (JsonObject)contentItem.Content, part.Path); + // Build the entries for this content item to evaluate for duplicates. + var entries = new List(); + await PopulateContainedContentItemRoutesAsync(entries, part.ContentItem.ContentItemId, containedAspect, (JsonObject)contentItem.Content, part.Path); - await ValidateContainedContentItemRoutesAsync(entries, part.ContentItem.ContentItemId, containedAspect, (JsonObject)contentItem.Content, part.Path); - } + await ValidateContainedContentItemRoutesAsync(entries, part.ContentItem.ContentItemId, containedAspect, (JsonObject)contentItem.Content, part.Path); + } - private async Task PopulateContainedContentItemRoutesAsync(List entries, string containerContentItemId, ContainedContentItemsAspect containedContentItemsAspect, JsonObject content, string basePath) + private async Task PopulateContainedContentItemRoutesAsync(List entries, string containerContentItemId, ContainedContentItemsAspect containedContentItemsAspect, JsonObject content, string basePath) + { + foreach (var accessor in containedContentItemsAspect.Accessors) { - foreach (var accessor in containedContentItemsAspect.Accessors) + var jItems = accessor.Invoke(content); + + foreach (var jItem in jItems.Cast()) { - var jItems = accessor.Invoke(content); + var contentItem = jItem.ToObject(); + var handlerAspect = await _contentManager.PopulateAspectAsync(contentItem); - foreach (var jItem in jItems.Cast()) + if (!handlerAspect.Disabled) { - var contentItem = jItem.ToObject(); - var handlerAspect = await _contentManager.PopulateAspectAsync(contentItem); - - if (!handlerAspect.Disabled) + var path = handlerAspect.Path; + if (!handlerAspect.Absolute) { - var path = handlerAspect.Path; - if (!handlerAspect.Absolute) - { - path = (basePath.EndsWith('/') ? basePath : basePath + '/') + handlerAspect.Path.TrimStart('/'); - } - - entries.Add(new AutorouteEntry(containerContentItemId, path, contentItem.ContentItemId, jItem.GetNormalizedPath()) - { - DocumentId = contentItem.Id - }); + path = (basePath.EndsWith('/') ? basePath : basePath + '/') + handlerAspect.Path.TrimStart('/'); } - var itemBasePath = (basePath.EndsWith('/') ? basePath : basePath + '/') + handlerAspect.Path.TrimStart('/'); - var childrenAspect = await _contentManager.PopulateAspectAsync(contentItem); - await PopulateContainedContentItemRoutesAsync(entries, containerContentItemId, childrenAspect, jItem, itemBasePath); + entries.Add(new AutorouteEntry(containerContentItemId, path, contentItem.ContentItemId, jItem.GetNormalizedPath()) + { + DocumentId = contentItem.Id + }); } + + var itemBasePath = (basePath.EndsWith('/') ? basePath : basePath + '/') + handlerAspect.Path.TrimStart('/'); + var childrenAspect = await _contentManager.PopulateAspectAsync(contentItem); + await PopulateContainedContentItemRoutesAsync(entries, containerContentItemId, childrenAspect, jItem, itemBasePath); } } + } - private async Task ValidateContainedContentItemRoutesAsync(List entries, string containerContentItemId, ContainedContentItemsAspect containedContentItemsAspect, JsonObject content, string basePath) + private async Task ValidateContainedContentItemRoutesAsync(List entries, string containerContentItemId, ContainedContentItemsAspect containedContentItemsAspect, JsonObject content, string basePath) + { + foreach (var accessor in containedContentItemsAspect.Accessors) { - foreach (var accessor in containedContentItemsAspect.Accessors) + var jItems = accessor.Invoke(content); + + foreach (var jItem in jItems.Cast()) { - var jItems = accessor.Invoke(content); + var contentItem = jItem.ToObject(); + var containedAutoroutePart = contentItem.As(); - foreach (var jItem in jItems.Cast()) + // This is only relevant if the content items have an autoroute part as we adjust the part value as required to guarantee a unique route. + // Content items routed only through the handler aspect already guarantee uniqueness. + if (containedAutoroutePart != null && !containedAutoroutePart.Disabled) { - var contentItem = jItem.ToObject(); - var containedAutoroutePart = contentItem.As(); + var path = containedAutoroutePart.Path; - // This is only relevant if the content items have an autoroute part as we adjust the part value as required to guarantee a unique route. - // Content items routed only through the handler aspect already guarantee uniqueness. - if (containedAutoroutePart != null && !containedAutoroutePart.Disabled) + if (containedAutoroutePart.Absolute && !await IsAbsolutePathUniqueAsync(path, contentItem.ContentItemId)) { - var path = containedAutoroutePart.Path; + path = await GenerateUniqueAbsolutePathAsync(path, contentItem.ContentItemId); + containedAutoroutePart.Path = path; + containedAutoroutePart.Apply(); - if (containedAutoroutePart.Absolute && !await IsAbsolutePathUniqueAsync(path, contentItem.ContentItemId)) + // Merge because we have disconnected the content item from it's json owner. + jItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings + { + MergeArrayHandling = MergeArrayHandling.Replace, + MergeNullValueHandling = MergeNullValueHandling.Merge + }); + } + else + { + var currentItemBasePath = basePath.EndsWith('/') ? basePath : basePath + '/'; + path = currentItemBasePath + containedAutoroutePart.Path.TrimStart('/'); + if (!IsRelativePathUnique(entries, path, containedAutoroutePart)) { - path = await GenerateUniqueAbsolutePathAsync(path, contentItem.ContentItemId); - containedAutoroutePart.Path = path; + path = GenerateRelativeUniquePath(entries, path, containedAutoroutePart); + // Remove base path and update part path. + containedAutoroutePart.Path = path[currentItemBasePath.Length..]; containedAutoroutePart.Apply(); // Merge because we have disconnected the content item from it's json owner. @@ -305,172 +323,153 @@ private async Task ValidateContainedContentItemRoutesAsync(List MergeNullValueHandling = MergeNullValueHandling.Merge }); } - else - { - var currentItemBasePath = basePath.EndsWith('/') ? basePath : basePath + '/'; - path = currentItemBasePath + containedAutoroutePart.Path.TrimStart('/'); - if (!IsRelativePathUnique(entries, path, containedAutoroutePart)) - { - path = GenerateRelativeUniquePath(entries, path, containedAutoroutePart); - // Remove base path and update part path. - containedAutoroutePart.Path = path[currentItemBasePath.Length..]; - containedAutoroutePart.Apply(); - - // Merge because we have disconnected the content item from it's json owner. - jItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings - { - MergeArrayHandling = MergeArrayHandling.Replace, - MergeNullValueHandling = MergeNullValueHandling.Merge - }); - } - - path = path[currentItemBasePath.Length..]; - } - var containedItemBasePath = (basePath.EndsWith('/') ? basePath : basePath + '/') + path; - var childItemAspect = await _contentManager.PopulateAspectAsync(contentItem); - await ValidateContainedContentItemRoutesAsync(entries, containerContentItemId, childItemAspect, jItem, containedItemBasePath); + path = path[currentItemBasePath.Length..]; } + + var containedItemBasePath = (basePath.EndsWith('/') ? basePath : basePath + '/') + path; + var childItemAspect = await _contentManager.PopulateAspectAsync(contentItem); + await ValidateContainedContentItemRoutesAsync(entries, containerContentItemId, childItemAspect, jItem, containedItemBasePath); } } } + } + + private static bool IsRelativePathUnique(List entries, string path, AutoroutePart context) + { + var result = !entries.Any(e => context.ContentItem.ContentItemId != e.ContainedContentItemId && string.Equals(e.Path.Trim('/'), path.Trim('/'), StringComparison.OrdinalIgnoreCase)); + return result; + } - private static bool IsRelativePathUnique(List entries, string path, AutoroutePart context) + private static string GenerateRelativeUniquePath(List entries, string path, AutoroutePart context) + { + var version = 1; + var unversionedPath = path; + + var versionSeparatorPosition = path.LastIndexOf('-'); + if (versionSeparatorPosition > -1 && int.TryParse(path[versionSeparatorPosition..].TrimStart('-'), out version)) { - var result = !entries.Any(e => context.ContentItem.ContentItemId != e.ContainedContentItemId && string.Equals(e.Path.Trim('/'), path.Trim('/'), StringComparison.OrdinalIgnoreCase)); - return result; + unversionedPath = path[..versionSeparatorPosition]; } - private static string GenerateRelativeUniquePath(List entries, string path, AutoroutePart context) + while (true) { - var version = 1; - var unversionedPath = path; - - var versionSeparatorPosition = path.LastIndexOf('-'); - if (versionSeparatorPosition > -1 && int.TryParse(path[versionSeparatorPosition..].TrimStart('-'), out version)) + // Unversioned length + separator char + version length. + var quantityCharactersToTrim = unversionedPath.Length + 1 + version.ToString().Length - AutoroutePart.MaxPathLength; + if (quantityCharactersToTrim > 0) { - unversionedPath = path[..versionSeparatorPosition]; + unversionedPath = unversionedPath[..^quantityCharactersToTrim]; } - while (true) + var versionedPath = $"{unversionedPath}-{version++}"; + if (IsRelativePathUnique(entries, versionedPath, context)) { - // Unversioned length + separator char + version length. - var quantityCharactersToTrim = unversionedPath.Length + 1 + version.ToString().Length - AutoroutePart.MaxPathLength; - if (quantityCharactersToTrim > 0) - { - unversionedPath = unversionedPath[..^quantityCharactersToTrim]; - } + var entry = entries.FirstOrDefault(e => e.ContainedContentItemId == context.ContentItem.ContentItemId); + entry.Path = versionedPath; - var versionedPath = $"{unversionedPath}-{version++}"; - if (IsRelativePathUnique(entries, versionedPath, context)) - { - var entry = entries.FirstOrDefault(e => e.ContainedContentItemId == context.ContentItem.ContentItemId); - entry.Path = versionedPath; - - return versionedPath; - } + return versionedPath; } } + } - private async Task GenerateContainerPathFromPatternAsync(AutoroutePart part) + private async Task GenerateContainerPathFromPatternAsync(AutoroutePart part) + { + // Compute the Path only if it's empty. + if (!string.IsNullOrWhiteSpace(part.Path)) { - // Compute the Path only if it's empty. - if (!string.IsNullOrWhiteSpace(part.Path)) - { - return; - } + return; + } - var pattern = await GetPatternAsync(part); + var pattern = await GetPatternAsync(part); - if (!string.IsNullOrEmpty(pattern)) + if (!string.IsNullOrEmpty(pattern)) + { + var model = new AutoroutePartViewModel() { - var model = new AutoroutePartViewModel() - { - Path = part.Path, - AutoroutePart = part, - ContentItem = part.ContentItem, - }; + Path = part.Path, + AutoroutePart = part, + ContentItem = part.ContentItem, + }; - _contentManager ??= _serviceProvider.GetRequiredService(); - - var cultureAspect = await _contentManager.PopulateAspectAsync(part.ContentItem, new CultureAspect()); + _contentManager ??= _serviceProvider.GetRequiredService(); - var cultureOptions = _serviceProvider.GetService>().Value; + var cultureAspect = await _contentManager.PopulateAspectAsync(part.ContentItem, new CultureAspect()); - using (CultureScope.Create(cultureAspect.Culture, ignoreSystemSettings: cultureOptions.IgnoreSystemSettings)) - { - part.Path = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, - new Dictionary() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) }); - } + var cultureOptions = _serviceProvider.GetService>().Value; - part.Path = part.Path.Replace("\r", string.Empty).Replace("\n", string.Empty); + using (CultureScope.Create(cultureAspect.Culture, ignoreSystemSettings: cultureOptions.IgnoreSystemSettings)) + { + part.Path = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, + new Dictionary() { [nameof(ContentItem)] = new ObjectValue(model.ContentItem) }); + } - if (part.Path?.Length > AutoroutePart.MaxPathLength) - { - part.Path = part.Path[..AutoroutePart.MaxPathLength]; - } + part.Path = part.Path.Replace("\r", string.Empty).Replace("\n", string.Empty); - if (!await IsAbsolutePathUniqueAsync(part.Path, part.ContentItem.ContentItemId)) - { - part.Path = await GenerateUniqueAbsolutePathAsync(part.Path, part.ContentItem.ContentItemId); - } + if (part.Path?.Length > AutoroutePart.MaxPathLength) + { + part.Path = part.Path[..AutoroutePart.MaxPathLength]; + } - part.Apply(); + if (!await IsAbsolutePathUniqueAsync(part.Path, part.ContentItem.ContentItemId)) + { + part.Path = await GenerateUniqueAbsolutePathAsync(part.Path, part.ContentItem.ContentItemId); } + + part.Apply(); } + } - /// - /// Get the pattern from the AutoroutePartSettings property for its type. - /// - private async Task GetPatternAsync(AutoroutePart part) - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(AutoroutePart), StringComparison.Ordinal)); - var pattern = contentTypePartDefinition.GetSettings().Pattern; + /// + /// Get the pattern from the AutoroutePartSettings property for its type. + /// + private async Task GetPatternAsync(AutoroutePart part) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(AutoroutePart), StringComparison.Ordinal)); + var pattern = contentTypePartDefinition.GetSettings().Pattern; - return pattern; - } + return pattern; + } + + private async Task GenerateUniqueAbsolutePathAsync(string path, string contentItemId) + { + var version = 1; + var unversionedPath = path; - private async Task GenerateUniqueAbsolutePathAsync(string path, string contentItemId) + var versionSeparatorPosition = path.LastIndexOf('-'); + if (versionSeparatorPosition > -1 && int.TryParse(path[versionSeparatorPosition..].TrimStart('-'), out version)) { - var version = 1; - var unversionedPath = path; + unversionedPath = path[..versionSeparatorPosition]; + } - var versionSeparatorPosition = path.LastIndexOf('-'); - if (versionSeparatorPosition > -1 && int.TryParse(path[versionSeparatorPosition..].TrimStart('-'), out version)) + while (true) + { + // Unversioned length + separator char + version length. + var quantityCharactersToTrim = unversionedPath.Length + 1 + version.ToString().Length - AutoroutePart.MaxPathLength; + if (quantityCharactersToTrim > 0) { - unversionedPath = path[..versionSeparatorPosition]; + unversionedPath = unversionedPath[..^quantityCharactersToTrim]; } - while (true) + var versionedPath = $"{unversionedPath}-{version++}"; + if (await IsAbsolutePathUniqueAsync(versionedPath, contentItemId)) { - // Unversioned length + separator char + version length. - var quantityCharactersToTrim = unversionedPath.Length + 1 + version.ToString().Length - AutoroutePart.MaxPathLength; - if (quantityCharactersToTrim > 0) - { - unversionedPath = unversionedPath[..^quantityCharactersToTrim]; - } - - var versionedPath = $"{unversionedPath}-{version++}"; - if (await IsAbsolutePathUniqueAsync(versionedPath, contentItemId)) - { - return versionedPath; - } + return versionedPath; } } + } - private async Task IsAbsolutePathUniqueAsync(string path, string contentItemId) - { - path = path.Trim('/'); - var paths = new string[] { path, "/" + path, path + "/", "/" + path + "/" }; - - var possibleConflicts = await _session.QueryIndex(o => (o.Published || o.Latest) && o.Path.IsIn(paths)).ListAsync(); - if (possibleConflicts.Any(x => x.ContentItemId != contentItemId && x.ContainedContentItemId != contentItemId)) - { - return false; - } + private async Task IsAbsolutePathUniqueAsync(string path, string contentItemId) + { + path = path.Trim('/'); + var paths = new string[] { path, "/" + path, path + "/", "/" + path + "/" }; - return true; + var possibleConflicts = await _session.QueryIndex(o => (o.Published || o.Latest) && o.Path.IsIn(paths)).ListAsync(); + if (possibleConflicts.Any(x => x.ContentItemId != contentItemId && x.ContainedContentItemId != contentItemId)) + { + return false; } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/DefaultRouteContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/DefaultRouteContentHandler.cs index 34b31c07313..0ff86f11a7d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/DefaultRouteContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Handlers/DefaultRouteContentHandler.cs @@ -3,36 +3,35 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Modules.Services; -namespace OrchardCore.Autoroute.Handlers +namespace OrchardCore.Autoroute.Handlers; + +public class DefaultRouteContentHandler : ContentHandlerBase { - public class DefaultRouteContentHandler : ContentHandlerBase - { - private readonly ISlugService _slugService; + private readonly ISlugService _slugService; - public DefaultRouteContentHandler(ISlugService slugService) - { - _slugService = slugService; - } + public DefaultRouteContentHandler(ISlugService slugService) + { + _slugService = slugService; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + { + return context.ForAsync(aspect => { - return context.ForAsync(aspect => + // Only use default aspect if no other handler has set the aspect. + if (string.IsNullOrEmpty(aspect.Path)) { - // Only use default aspect if no other handler has set the aspect. - if (string.IsNullOrEmpty(aspect.Path)) + // By default contained route is content item id + display text, if present. + var path = context.ContentItem.ContentItemId; + if (!string.IsNullOrEmpty(context.ContentItem.DisplayText)) { - // By default contained route is content item id + display text, if present. - var path = context.ContentItem.ContentItemId; - if (!string.IsNullOrEmpty(context.ContentItem.DisplayText)) - { - path = path + "-" + context.ContentItem.DisplayText; - } - - aspect.Path = _slugService.Slugify(path); + path = path + "-" + context.ContentItem.DisplayText; } - return Task.CompletedTask; - }); - } + aspect.Path = _slugService.Slugify(path); + } + + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Indexing/AutoroutePartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Indexing/AutoroutePartIndexHandler.cs index 60a089e0490..11d7e0d6fc7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Indexing/AutoroutePartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Indexing/AutoroutePartIndexHandler.cs @@ -2,22 +2,21 @@ using OrchardCore.Autoroute.Models; using OrchardCore.Indexing; -namespace OrchardCore.Autoroute.Indexing +namespace OrchardCore.Autoroute.Indexing; + +public class AutoroutePartIndexHandler : ContentPartIndexHandler { - public class AutoroutePartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(AutoroutePart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(AutoroutePart part, BuildPartIndexContext context) - { - var options = context.Settings.ToOptions() - & ~DocumentIndexOptions.Sanitize - ; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, part.Path, options); - } + var options = context.Settings.ToOptions() + & ~DocumentIndexOptions.Sanitize + ; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, part.Path, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Migrations.cs index 21aaf5a18d1..928bc54ff53 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Migrations.cs @@ -6,77 +6,76 @@ using OrchardCore.Data.Migration; using YesSql.Sql; -namespace OrchardCore.Autoroute +namespace OrchardCore.Autoroute; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("AutoroutePart", builder => builder - .Attachable() - .WithDescription("Provides a custom url for your content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("AutoroutePart", builder => builder + .Attachable() + .WithDescription("Provides a custom url for your content item.")); - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", c => c.WithLength(26)) - .Column("ContainedContentItemId", c => c.WithLength(26)) - .Column("JsonPath", c => c.Unlimited()) - .Column("Path", col => col.WithLength(AutoroutePart.MaxPathLength)) - .Column("Published") - .Column("Latest") - ); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", c => c.WithLength(26)) + .Column("ContainedContentItemId", c => c.WithLength(26)) + .Column("JsonPath", c => c.Unlimited()) + .Column("Path", col => col.WithLength(AutoroutePart.MaxPathLength)) + .Column("Published") + .Column("Latest") + ); - // Shortcut other migration steps on new content definition schemas. - return 5; - } + // Shortcut other migration steps on new content definition schemas. + return 5; + } - // Migrate PartSettings. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.MigratePartSettingsAsync(); + // Migrate PartSettings. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _contentDefinitionManager.MigratePartSettingsAsync(); - return 2; - } + return 2; + } - // This code can be removed in a later version. + // This code can be removed in a later version. #pragma warning disable CA1822 // Mark members as static - public int UpdateFrom2() + public int UpdateFrom2() #pragma warning restore CA1822 // Mark members as static - { - return 3; - } + { + return 3; + } - // This code can be removed in a later version. - public async Task UpdateFrom3Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("ContainedContentItemId", c => c.WithLength(26)) - ); + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("ContainedContentItemId", c => c.WithLength(26)) + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("JsonPath", c => c.Unlimited()) - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("JsonPath", c => c.Unlimited()) + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Latest", c => c.WithDefault(false)) - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Latest", c => c.WithDefault(false)) + ); - return 4; - } + return 4; + } - // This code can be removed in a later version. + // This code can be removed in a later version. #pragma warning disable CA1822 // Mark members as static - public int UpdateFrom4() + public int UpdateFrom4() #pragma warning restore CA1822 // Mark members as static - { - return 5; - } + { + return 5; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartExtensions.cs index ffb16a5a37d..9b46e0e67b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartExtensions.cs @@ -4,48 +4,47 @@ using System.Linq; using Microsoft.Extensions.Localization; -namespace OrchardCore.Autoroute.Models +namespace OrchardCore.Autoroute.Models; + +public static class AutoroutePartExtensions { - public static class AutoroutePartExtensions + public static IEnumerable ValidatePathFieldValue(this AutoroutePart autoroute, IStringLocalizer S) + { + if (autoroute.Path == "/") + { + yield return new ValidationResult(S["Your permalink can't be set to the homepage, please use the homepage option instead."], new[] { nameof(autoroute.Path) }); + } + + if (HasInvalidCharacters(autoroute.Path)) + { + var invalidCharactersForMessage = string.Join(", ", AutoroutePart.InvalidCharactersForPath.Select(c => $"\"{c}\"")); + yield return new ValidationResult(S["Please do not use any of the following characters in your permalink: {0}. No spaces, or consecutive slashes, are allowed (please use dashes or underscores instead).", invalidCharactersForMessage], new[] { nameof(autoroute.Path) }); + } + + if (autoroute.Path?.Length > AutoroutePart.MaxPathLength) + { + yield return new ValidationResult(S["Your permalink is too long. The permalink can only be up to {0} characters.", AutoroutePart.MaxPathLength], new[] { nameof(autoroute.Path) }); + } + } + + private static bool HasInvalidCharacters(string path) { - public static IEnumerable ValidatePathFieldValue(this AutoroutePart autoroute, IStringLocalizer S) + // IndexOfAny performs culture-insensitive and case-sensitive search. + if (path?.IndexOfAny(AutoroutePart.InvalidCharactersForPath) > -1) { - if (autoroute.Path == "/") - { - yield return new ValidationResult(S["Your permalink can't be set to the homepage, please use the homepage option instead."], new[] { nameof(autoroute.Path) }); - } - - if (HasInvalidCharacters(autoroute.Path)) - { - var invalidCharactersForMessage = string.Join(", ", AutoroutePart.InvalidCharactersForPath.Select(c => $"\"{c}\"")); - yield return new ValidationResult(S["Please do not use any of the following characters in your permalink: {0}. No spaces, or consecutive slashes, are allowed (please use dashes or underscores instead).", invalidCharactersForMessage], new[] { nameof(autoroute.Path) }); - } - - if (autoroute.Path?.Length > AutoroutePart.MaxPathLength) - { - yield return new ValidationResult(S["Your permalink is too long. The permalink can only be up to {0} characters.", AutoroutePart.MaxPathLength], new[] { nameof(autoroute.Path) }); - } + return true; } - private static bool HasInvalidCharacters(string path) + if (path?.IndexOf(' ', StringComparison.Ordinal) > -1) { - // IndexOfAny performs culture-insensitive and case-sensitive search. - if (path?.IndexOfAny(AutoroutePart.InvalidCharactersForPath) > -1) - { - return true; - } - - if (path?.IndexOf(' ', StringComparison.Ordinal) > -1) - { - return true; - } - - if (path?.IndexOf("//", StringComparison.Ordinal) > -1) - { - return true; - } - - return false; + return true; } + + if (path?.IndexOf("//", StringComparison.Ordinal) > -1) + { + return true; + } + + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartSettings.cs index 0a988e0b686..a9eb56c61ab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Models/AutoroutePartSettings.cs @@ -1,48 +1,47 @@ using System.ComponentModel; -namespace OrchardCore.Autoroute.Models +namespace OrchardCore.Autoroute.Models; + +public class AutoroutePartSettings { - public class AutoroutePartSettings - { - /// - /// Gets or sets whether a user can define a custom path. - /// - public bool AllowCustomPath { get; set; } - - /// - /// The pattern used to build the Path. - /// - [DefaultValue("{{ ContentItem.DisplayText | slugify }}")] - public string Pattern { get; set; } = "{{ ContentItem.DisplayText | slugify }}"; - - /// - /// Whether to display an option to set the content item as the homepage. - /// - public bool ShowHomepageOption { get; set; } - - /// - /// Whether a user can request a new path if some data has changed. - /// - public bool AllowUpdatePath { get; set; } - - /// - /// Whether a user is allowed to disable autoute generation to content items of this type. - /// - public bool AllowDisabled { get; set; } - - /// - /// Whether to allow routing of contained content items. - /// - public bool AllowRouteContainedItems { get; set; } - - /// - /// Whether this part is managing contained item routes. - /// - public bool ManageContainedItemRoutes { get; set; } - - /// - /// Whether to allow routing of contained content items to absolute paths. - /// - public bool AllowAbsolutePath { get; set; } - } + /// + /// Gets or sets whether a user can define a custom path. + /// + public bool AllowCustomPath { get; set; } + + /// + /// The pattern used to build the Path. + /// + [DefaultValue("{{ ContentItem.DisplayText | slugify }}")] + public string Pattern { get; set; } = "{{ ContentItem.DisplayText | slugify }}"; + + /// + /// Whether to display an option to set the content item as the homepage. + /// + public bool ShowHomepageOption { get; set; } + + /// + /// Whether a user can request a new path if some data has changed. + /// + public bool AllowUpdatePath { get; set; } + + /// + /// Whether a user is allowed to disable autoute generation to content items of this type. + /// + public bool AllowDisabled { get; set; } + + /// + /// Whether to allow routing of contained content items. + /// + public bool AllowRouteContainedItems { get; set; } + + /// + /// Whether this part is managing contained item routes. + /// + public bool ManageContainedItemRoutes { get; set; } + + /// + /// Whether to allow routing of contained content items to absolute paths. + /// + public bool AllowAbsolutePath { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/AutorouteMetaWeblogDriver.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/AutorouteMetaWeblogDriver.cs index e42acbae61b..b4ab8cf12ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/AutorouteMetaWeblogDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/AutorouteMetaWeblogDriver.cs @@ -5,36 +5,35 @@ using OrchardCore.XmlRpc; using OrchardCore.XmlRpc.Models; -namespace OrchardCore.Autoroute.RemotePublishing +namespace OrchardCore.Autoroute.RemotePublishing; + +public sealed class AutorouteMetaWeblogDriver : MetaWeblogDriver { - public sealed class AutorouteMetaWeblogDriver : MetaWeblogDriver + public override void SetCapabilities(Action setCapability) { - public override void SetCapabilities(Action setCapability) - { - setCapability("supportsSlug", "Yes"); - } + setCapability("supportsSlug", "Yes"); + } - public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) + public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) + { + var autoroutePart = contentItem.As(); + if (autoroutePart == null) { - var autoroutePart = contentItem.As(); - if (autoroutePart == null) - { - return; - } - - rpcStruct.Set("wp_slug", autoroutePart.Path); + return; } - public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + rpcStruct.Set("wp_slug", autoroutePart.Path); + } + + public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + { + if (contentItem.As() != null) { - if (contentItem.As() != null) - { - var slug = rpcStruct.Optional("wp_slug"); + var slug = rpcStruct.Optional("wp_slug"); - if (!string.IsNullOrWhiteSpace(slug)) - { - contentItem.Alter(x => x.Path = slug); - } + if (!string.IsNullOrWhiteSpace(slug)) + { + contentItem.Alter(x => x.Path = slug); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/RemotePublishingStartup.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/RemotePublishingStartup.cs index 1287b724ac3..7adb27af96f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/RemotePublishingStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/RemotePublishing/RemotePublishingStartup.cs @@ -2,14 +2,13 @@ using OrchardCore.MetaWeblog; using OrchardCore.Modules; -namespace OrchardCore.Autoroute.RemotePublishing +namespace OrchardCore.Autoroute.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class RemotePublishingStartup : StartupBase { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class RemotePublishingStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteTransformer.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteTransformer.cs index 00366911063..810490a5858 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteTransformer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteTransformer.cs @@ -5,43 +5,42 @@ using Microsoft.Extensions.Options; using OrchardCore.ContentManagement.Routing; -namespace OrchardCore.Autoroute.Routing +namespace OrchardCore.Autoroute.Routing; + +public class AutorouteTransformer : DynamicRouteValueTransformer { - public class AutorouteTransformer : DynamicRouteValueTransformer + private readonly IAutorouteEntries _entries; + private readonly AutorouteOptions _options; + + public AutorouteTransformer(IAutorouteEntries entries, IOptions options) { - private readonly IAutorouteEntries _entries; - private readonly AutorouteOptions _options; + _entries = entries; + _options = options.Value; + } - public AutorouteTransformer(IAutorouteEntries entries, IOptions options) - { - _entries = entries; - _options = options.Value; - } + public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + (var found, var entry) = await _entries.TryGetEntryByPathAsync(httpContext.Request.Path.Value); - public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + if (found) { - (var found, var entry) = await _entries.TryGetEntryByPathAsync(httpContext.Request.Path.Value); - - if (found) + var routeValues = new RouteValueDictionary(_options.GlobalRouteValues) { - var routeValues = new RouteValueDictionary(_options.GlobalRouteValues) - { - [_options.ContentItemIdKey] = entry.ContentItemId - }; - - if (!string.IsNullOrEmpty(entry.JsonPath)) - { - routeValues[_options.JsonPathKey] = entry.JsonPath; - } + [_options.ContentItemIdKey] = entry.ContentItemId + }; - // Prevents the original values from being re-added to the dynamic ones. - values.Clear(); + if (!string.IsNullOrEmpty(entry.JsonPath)) + { + routeValues[_options.JsonPathKey] = entry.JsonPath; + } - return routeValues; + // Prevents the original values from being re-added to the dynamic ones. + values.Clear(); - } + return routeValues; - return null; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteValuesAddressScheme.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteValuesAddressScheme.cs index 5a21d7d56c5..427c5a23d4e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteValuesAddressScheme.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Routing/AutorouteValuesAddressScheme.cs @@ -7,94 +7,93 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Routing; -namespace OrchardCore.Autoroute.Routing +namespace OrchardCore.Autoroute.Routing; + +internal sealed class AutorouteValuesAddressScheme : IShellRouteValuesAddressScheme { - internal sealed class AutorouteValuesAddressScheme : IShellRouteValuesAddressScheme + private readonly IAutorouteEntries _entries; + private readonly AutorouteOptions _options; + + public AutorouteValuesAddressScheme(IAutorouteEntries entries, IOptions options) { - private readonly IAutorouteEntries _entries; - private readonly AutorouteOptions _options; + _entries = entries; + _options = options.Value; + } - public AutorouteValuesAddressScheme(IAutorouteEntries entries, IOptions options) + public IEnumerable FindEndpoints(RouteValuesAddress address) + { + if (address.AmbientValues == null || address.ExplicitValues == null) { - _entries = entries; - _options = options.Value; + return []; } - public IEnumerable FindEndpoints(RouteValuesAddress address) + // Try to get the contained item first, then the container content item + var contentItemId = address.ExplicitValues[_options.ContainedContentItemIdKey]?.ToString(); + if (string.IsNullOrEmpty(contentItemId)) { - if (address.AmbientValues == null || address.ExplicitValues == null) - { - return []; - } - - // Try to get the contained item first, then the container content item - var contentItemId = address.ExplicitValues[_options.ContainedContentItemIdKey]?.ToString(); - if (string.IsNullOrEmpty(contentItemId)) - { - contentItemId = address.ExplicitValues[_options.ContentItemIdKey]?.ToString(); - } + contentItemId = address.ExplicitValues[_options.ContentItemIdKey]?.ToString(); + } - if (string.IsNullOrEmpty(contentItemId)) - { - return []; - } + if (string.IsNullOrEmpty(contentItemId)) + { + return []; + } - (var found, var autorouteEntry) = _entries.TryGetEntryByContentItemIdAsync(contentItemId).GetAwaiter().GetResult(); + (var found, var autorouteEntry) = _entries.TryGetEntryByContentItemIdAsync(contentItemId).GetAwaiter().GetResult(); - if (!found) - { - return []; - } + if (!found) + { + return []; + } - if (Match(address.ExplicitValues)) - { - // Once we have the contained content item id value we no longer want it in the route values. - address.ExplicitValues.Remove(_options.ContainedContentItemIdKey); + if (Match(address.ExplicitValues)) + { + // Once we have the contained content item id value we no longer want it in the route values. + address.ExplicitValues.Remove(_options.ContainedContentItemIdKey); - var routeValues = new RouteValueDictionary(address.ExplicitValues); + var routeValues = new RouteValueDictionary(address.ExplicitValues); - if (address.ExplicitValues.Count > _options.GlobalRouteValues.Count + 1) + if (address.ExplicitValues.Count > _options.GlobalRouteValues.Count + 1) + { + foreach (var entry in address.ExplicitValues) { - foreach (var entry in address.ExplicitValues) + if (string.Equals(entry.Key, _options.ContentItemIdKey, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(entry.Key, _options.ContentItemIdKey, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!_options.GlobalRouteValues.ContainsKey(entry.Key)) - { - routeValues.Remove(entry.Key); - } + continue; } - } - - var endpoint = new RouteEndpoint - ( - c => null, - RoutePatternFactory.Parse(autorouteEntry.Path, routeValues, null), - 0, - null, - null - ); - return new[] { endpoint }; + if (!_options.GlobalRouteValues.ContainsKey(entry.Key)) + { + routeValues.Remove(entry.Key); + } + } } - return []; + var endpoint = new RouteEndpoint + ( + c => null, + RoutePatternFactory.Parse(autorouteEntry.Path, routeValues, null), + 0, + null, + null + ); + + return new[] { endpoint }; } - private bool Match(RouteValueDictionary explicitValues) + return []; + } + + private bool Match(RouteValueDictionary explicitValues) + { + foreach (var entry in _options.GlobalRouteValues) { - foreach (var entry in _options.GlobalRouteValues) + if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) - { - return false; - } + return false; } - - return true; } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Settings/AutoroutePartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Settings/AutoroutePartSettingsDisplayDriver.cs index cbd27a10902..678f27a1951 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Settings/AutoroutePartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Settings/AutoroutePartSettingsDisplayDriver.cs @@ -8,74 +8,73 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Liquid; -namespace OrchardCore.Autoroute.Settings +namespace OrchardCore.Autoroute.Settings; + +public sealed class AutoroutePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class AutoroutePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly ILiquidTemplateManager _templateManager; + private readonly ILiquidTemplateManager _templateManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AutoroutePartSettingsDisplayDriver( - ILiquidTemplateManager templateManager, - IStringLocalizer localizer) - { - _templateManager = templateManager; - S = localizer; - } + public AutoroutePartSettingsDisplayDriver( + ILiquidTemplateManager templateManager, + IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("AutoroutePartSettings_Edit", model => { - return Initialize("AutoroutePartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.AllowCustomPath = settings.AllowCustomPath; - model.AllowUpdatePath = settings.AllowUpdatePath; - model.Pattern = settings.Pattern; - model.ShowHomepageOption = settings.ShowHomepageOption; - model.AllowDisabled = settings.AllowDisabled; - model.AllowRouteContainedItems = settings.AllowRouteContainedItems; - model.ManageContainedItemRoutes = settings.ManageContainedItemRoutes; - model.AllowAbsolutePath = settings.AllowAbsolutePath; - model.AutoroutePartSettings = settings; - }).Location("Content"); - } + model.AllowCustomPath = settings.AllowCustomPath; + model.AllowUpdatePath = settings.AllowUpdatePath; + model.Pattern = settings.Pattern; + model.ShowHomepageOption = settings.ShowHomepageOption; + model.AllowDisabled = settings.AllowDisabled; + model.AllowRouteContainedItems = settings.AllowRouteContainedItems; + model.ManageContainedItemRoutes = settings.ManageContainedItemRoutes; + model.AllowAbsolutePath = settings.AllowAbsolutePath; + model.AutoroutePartSettings = settings; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new AutoroutePartSettingsViewModel(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new AutoroutePartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Pattern, - m => m.AllowCustomPath, - m => m.AllowUpdatePath, - m => m.ShowHomepageOption, - m => m.AllowDisabled, - m => m.AllowRouteContainedItems, - m => m.ManageContainedItemRoutes, - m => m.AllowAbsolutePath); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Pattern, + m => m.AllowCustomPath, + m => m.AllowUpdatePath, + m => m.ShowHomepageOption, + m => m.AllowDisabled, + m => m.AllowRouteContainedItems, + m => m.ManageContainedItemRoutes, + m => m.AllowAbsolutePath); - if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) - { - context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else + if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) + { + context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + else + { + context.Builder.WithSettings(new AutoroutePartSettings { - context.Builder.WithSettings(new AutoroutePartSettings - { - Pattern = model.Pattern, - AllowCustomPath = model.AllowCustomPath, - AllowUpdatePath = model.AllowUpdatePath, - ShowHomepageOption = model.ShowHomepageOption, - AllowDisabled = model.AllowDisabled, - AllowRouteContainedItems = model.AllowRouteContainedItems, - ManageContainedItemRoutes = model.ManageContainedItemRoutes, - AllowAbsolutePath = model.AllowAbsolutePath - }); - } - - return Edit(contentTypePartDefinition, context); + Pattern = model.Pattern, + AllowCustomPath = model.AllowCustomPath, + AllowUpdatePath = model.AllowUpdatePath, + ShowHomepageOption = model.ShowHomepageOption, + AllowDisabled = model.AllowDisabled, + AllowRouteContainedItems = model.AllowRouteContainedItems, + ManageContainedItemRoutes = model.ManageContainedItemRoutes, + AllowAbsolutePath = model.AllowAbsolutePath + }); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Sitemaps/AutorouteContentTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Sitemaps/AutorouteContentTypeProvider.cs index 2d256e7a71d..4bf2e4aa2de 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Sitemaps/AutorouteContentTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Sitemaps/AutorouteContentTypeProvider.cs @@ -9,43 +9,42 @@ using OrchardCore.Sitemaps.Builders; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Autoroute.Sitemaps +namespace OrchardCore.Autoroute.Sitemaps; + +public class AutorouteContentTypeProvider : IRouteableContentTypeProvider { - public class AutorouteContentTypeProvider : IRouteableContentTypeProvider + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + + public AutorouteContentTypeProvider( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + } - public AutorouteContentTypeProvider( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager - ) - { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - } + public async Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) + { + var ctd = (await ListRoutableTypeDefinitionsAsync())?.FirstOrDefault(ctd => ctd.Name == contentItem.ContentType); - public async Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) + if (ctd != null) { - var ctd = (await ListRoutableTypeDefinitionsAsync())?.FirstOrDefault(ctd => ctd.Name == contentItem.ContentType); - - if (ctd != null) - { - var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); - var routes = contentItemMetadata.DisplayRouteValues; + var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); + var routes = contentItemMetadata.DisplayRouteValues; - // UrlHelper.Action includes BasePath automatically if present. - // If content item is assigned as home route, UrlHelper resolves as site root. - return context.HostPrefix + context.UrlHelper.Action(routes["Action"].ToString(), routes); - } - - return null; + // UrlHelper.Action includes BasePath automatically if present. + // If content item is assigned as home route, UrlHelper resolves as site root. + return context.HostPrefix + context.UrlHelper.Action(routes["Action"].ToString(), routes); } - public async Task> ListRoutableTypeDefinitionsAsync() - { - return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(ctd => ctd.Parts.Any(p => p.Name == nameof(AutoroutePart))); - } + return null; + } + + public async Task> ListRoutableTypeDefinitionsAsync() + { + return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(ctd => ctd.Parts.Any(p => p.Name == nameof(AutoroutePart))); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/Startup.cs index e17a93efffb..4f28e7bd4fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/Startup.cs @@ -29,89 +29,88 @@ using OrchardCore.Security.Permissions; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Autoroute +namespace OrchardCore.Autoroute; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - public override int ConfigureOrder - => OrchardCoreConstants.ConfigureOrder.Autoroute; + public override int ConfigureOrder + => OrchardCoreConstants.ConfigureOrder.Autoroute; - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(o => { - services.Configure(o => + o.MemberAccessStrategy.Register(); + + o.MemberAccessStrategy.Register("Slug", (obj, context) => { - o.MemberAccessStrategy.Register(); + var liquidTemplateContext = (LiquidTemplateContext)context; - o.MemberAccessStrategy.Register("Slug", (obj, context) => + return new LiquidPropertyAccessor(liquidTemplateContext, async (slug, context) => { - var liquidTemplateContext = (LiquidTemplateContext)context; + var autorouteEntries = context.Services.GetRequiredService(); + var contentManager = context.Services.GetRequiredService(); - return new LiquidPropertyAccessor(liquidTemplateContext, async (slug, context) => + if (!slug.StartsWith('/')) { - var autorouteEntries = context.Services.GetRequiredService(); - var contentManager = context.Services.GetRequiredService(); + slug = "/" + slug; + } - if (!slug.StartsWith('/')) - { - slug = "/" + slug; - } + (var found, var entry) = await autorouteEntries.TryGetEntryByPathAsync(slug); - (var found, var entry) = await autorouteEntries.TryGetEntryByPathAsync(slug); - - if (found) - { - return FluidValue.Create(await contentManager.GetAsync(entry.ContentItemId, entry.JsonPath), context.Options); - } + if (found) + { + return FluidValue.Create(await contentManager.GetAsync(entry.ContentItemId, entry.JsonPath), context.Options); + } - return NilValue.Instance; - }); + return NilValue.Instance; }); }); + }); - // Autoroute Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Autoroute Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); - services.AddDataMigration(); - services.AddSingleton(); - services.AddScoped(); + services.AddDataMigration(); + services.AddSingleton(); + services.AddScoped(); - services.Configure(options => + services.Configure(options => + { + options.ConfigurePart(partOptions => { - options.ConfigurePart(partOptions => - { - partOptions.Collapse = true; - }); + partOptions.Collapse = true; }); + }); - services.AddSingleton(); - services.AddSingleton(); - } + services.AddSingleton(); + services.AddSingleton(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - // The 1st segment prevents the transformer to be executed for the home. - routes.MapDynamicControllerRoute("/{any}/{**slug}"); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + // The 1st segment prevents the transformer to be executed for the home. + routes.MapDynamicControllerRoute("/{any}/{**slug}"); } +} - [RequireFeatures("OrchardCore.Sitemaps")] - public class SitemapStartup : StartupBase +[RequireFeatures("OrchardCore.Sitemaps")] +public class SitemapStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartSettingsViewModel.cs index 4860f6710c7..cecaaaa1edc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartSettingsViewModel.cs @@ -1,17 +1,16 @@ using OrchardCore.Autoroute.Models; -namespace OrchardCore.Autoroute.ViewModels +namespace OrchardCore.Autoroute.ViewModels; + +public class AutoroutePartSettingsViewModel { - public class AutoroutePartSettingsViewModel - { - public bool AllowCustomPath { get; set; } - public string Pattern { get; set; } - public bool ShowHomepageOption { get; set; } - public bool AllowUpdatePath { get; set; } - public bool AllowDisabled { get; set; } - public bool AllowRouteContainedItems { get; set; } - public bool ManageContainedItemRoutes { get; set; } - public bool AllowAbsolutePath { get; set; } - public AutoroutePartSettings AutoroutePartSettings { get; set; } - } + public bool AllowCustomPath { get; set; } + public string Pattern { get; set; } + public bool ShowHomepageOption { get; set; } + public bool AllowUpdatePath { get; set; } + public bool AllowDisabled { get; set; } + public bool AllowRouteContainedItems { get; set; } + public bool ManageContainedItemRoutes { get; set; } + public bool AllowAbsolutePath { get; set; } + public AutoroutePartSettings AutoroutePartSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartViewModel.cs index 8dbda234584..43a224ad8cf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Autoroute/ViewModels/AutoroutePartViewModel.cs @@ -2,25 +2,24 @@ using OrchardCore.Autoroute.Models; using OrchardCore.ContentManagement; -namespace OrchardCore.Autoroute.ViewModels +namespace OrchardCore.Autoroute.ViewModels; + +public class AutoroutePartViewModel { - public class AutoroutePartViewModel - { - public string Path { get; set; } - public bool SetHomepage { get; set; } - public bool UpdatePath { get; set; } - public bool IsHomepage { get; set; } - public bool RouteContainedItems { get; set; } - public bool Absolute { get; set; } - public bool Disabled { get; set; } + public string Path { get; set; } + public bool SetHomepage { get; set; } + public bool UpdatePath { get; set; } + public bool IsHomepage { get; set; } + public bool RouteContainedItems { get; set; } + public bool Absolute { get; set; } + public bool Disabled { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public AutoroutePart AutoroutePart { get; set; } + [BindNever] + public AutoroutePart AutoroutePart { get; set; } - [BindNever] - public AutoroutePartSettings Settings { get; set; } - } + [BindNever] + public AutoroutePartSettings Settings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/AdminMenu.cs index 2c850767809..d31af77973b 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/AdminMenu.cs @@ -2,33 +2,32 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.BackgroundTasks +namespace OrchardCore.BackgroundTasks; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) => S = localizer; + public AdminMenu(IStringLocalizer localizer) => S = localizer; - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Tasks"], S["Tasks"].PrefixPosition(), tasks => tasks - .Add(S["Background Tasks"], S["Background Tasks"].PrefixPosition(), backgroundTasks => backgroundTasks - .Action("Index", "BackgroundTask", "OrchardCore.BackgroundTasks") - .Permission(Permissions.ManageBackgroundTasks) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Tasks"], S["Tasks"].PrefixPosition(), tasks => tasks + .Add(S["Background Tasks"], S["Background Tasks"].PrefixPosition(), backgroundTasks => backgroundTasks + .Action("Index", "BackgroundTask", "OrchardCore.BackgroundTasks") + .Permission(Permissions.ManageBackgroundTasks) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Controllers/BackgroundTaskController.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Controllers/BackgroundTaskController.cs index 68bf3b80fd2..9cf15c878d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Controllers/BackgroundTaskController.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Controllers/BackgroundTaskController.cs @@ -18,271 +18,270 @@ using OrchardCore.Navigation; using OrchardCore.Routing; -namespace OrchardCore.BackgroundTasks.Controllers +namespace OrchardCore.BackgroundTasks.Controllers; + +[Admin("BackgroundTasks/{action}/{name?}", "BackgroundTasks{action}")] +public class BackgroundTaskController : Controller { - [Admin("BackgroundTasks/{action}/{name?}", "BackgroundTasks{action}")] - public class BackgroundTaskController : Controller + private const string _optionsSearch = $"{nameof(BackgroundTaskIndexViewModel.Options)}.{nameof(AdminIndexOptions.Search)}"; + private const string _optionsStatus = $"{nameof(BackgroundTaskIndexViewModel.Options)}.{nameof(AdminIndexOptions.Status)}"; + + private readonly IAuthorizationService _authorizationService; + private readonly IEnumerable _backgroundTasks; + private readonly BackgroundTaskManager _backgroundTaskManager; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public BackgroundTaskController( + IAuthorizationService authorizationService, + IEnumerable backgroundTasks, + BackgroundTaskManager backgroundTaskManager, + IOptions pagerOptions, + IShapeFactory shapeFactory, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + INotifier notifier) + { + _authorizationService = authorizationService; + _backgroundTasks = backgroundTasks; + _backgroundTaskManager = backgroundTaskManager; + _pagerOptions = pagerOptions.Value; + _notifier = notifier; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + } + + [Admin("BackgroundTasks", "BackgroundTasks")] + public async Task Index(AdminIndexOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = $"{nameof(BackgroundTaskIndexViewModel.Options)}.{nameof(AdminIndexOptions.Search)}"; - private const string _optionsStatus = $"{nameof(BackgroundTaskIndexViewModel.Options)}.{nameof(AdminIndexOptions.Status)}"; - - private readonly IAuthorizationService _authorizationService; - private readonly IEnumerable _backgroundTasks; - private readonly BackgroundTaskManager _backgroundTaskManager; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public BackgroundTaskController( - IAuthorizationService authorizationService, - IEnumerable backgroundTasks, - BackgroundTaskManager backgroundTaskManager, - IOptions pagerOptions, - IShapeFactory shapeFactory, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer, - INotifier notifier) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) { - _authorizationService = authorizationService; - _backgroundTasks = backgroundTasks; - _backgroundTaskManager = backgroundTaskManager; - _pagerOptions = pagerOptions.Value; - _notifier = notifier; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - [Admin("BackgroundTasks", "BackgroundTasks")] - public async Task Index(AdminIndexOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) - { - return Forbid(); - } + var document = await _backgroundTaskManager.GetDocumentAsync(); - var document = await _backgroundTaskManager.GetDocumentAsync(); + var items = _backgroundTasks.Select(task => + { + var defaultSettings = task.GetDefaultSettings(); - var items = _backgroundTasks.Select(task => + if (document.Settings.TryGetValue(task.GetTaskName(), out var settings)) { - var defaultSettings = task.GetDefaultSettings(); - - if (document.Settings.TryGetValue(task.GetTaskName(), out var settings)) - { - return new BackgroundTaskEntry() - { - Name = defaultSettings.Name, - Title = defaultSettings.Title, - Description = settings.Description, - Enable = settings.Enable, - }; - } - return new BackgroundTaskEntry() { Name = defaultSettings.Name, Title = defaultSettings.Title, - Description = defaultSettings.Description, - Enable = defaultSettings.Enable, + Description = settings.Description, + Enable = settings.Enable, }; - }); - - if (!string.IsNullOrWhiteSpace(options.Search)) - { - items = items.Where(entry => entry.Title != null && entry.Title.Contains(options.Search, StringComparison.OrdinalIgnoreCase) - || (entry.Description != null && entry.Description.Contains(options.Search, StringComparison.OrdinalIgnoreCase)) - ); } - if (string.Equals(options.Status, "enabled", StringComparison.OrdinalIgnoreCase)) + return new BackgroundTaskEntry() { - items = items.Where(entry => entry.Enable); - } - else if (string.Equals(options.Status, "disabled", StringComparison.OrdinalIgnoreCase)) - { - items = items.Where(entry => !entry.Enable); - } - - options.Statuses = - [ - new SelectListItem(S["Enabled"], "enabled"), - new SelectListItem(S["Disabled"], "disabled") - ]; + Name = defaultSettings.Name, + Title = defaultSettings.Title, + Description = defaultSettings.Description, + Enable = defaultSettings.Enable, + }; + }); - var taskItems = items.ToList(); - var routeData = new RouteData(); + if (!string.IsNullOrWhiteSpace(options.Search)) + { + items = items.Where(entry => entry.Title != null && entry.Title.Contains(options.Search, StringComparison.OrdinalIgnoreCase) + || (entry.Description != null && entry.Description.Contains(options.Search, StringComparison.OrdinalIgnoreCase)) + ); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + if (string.Equals(options.Status, "enabled", StringComparison.OrdinalIgnoreCase)) + { + items = items.Where(entry => entry.Enable); + } + else if (string.Equals(options.Status, "disabled", StringComparison.OrdinalIgnoreCase)) + { + items = items.Where(entry => !entry.Enable); + } - if (!string.IsNullOrEmpty(options.Status)) - { - routeData.Values.TryAdd(_optionsStatus, options.Status); - } + options.Statuses = + [ + new SelectListItem(S["Enabled"], "enabled"), + new SelectListItem(S["Disabled"], "disabled") + ]; - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var pagerShape = await _shapeFactory.PagerAsync(pager, taskItems.Count, routeData); + var taskItems = items.ToList(); + var routeData = new RouteData(); - var model = new BackgroundTaskIndexViewModel - { - Tasks = taskItems.OrderBy(entry => entry.Title).Skip(pager.GetStartIndex()).Take(pager.PageSize).ToList(), - Pager = pagerShape, - Options = options, - }; + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - return View(model); + if (!string.IsNullOrEmpty(options.Status)) + { + routeData.Values.TryAdd(_optionsStatus, options.Status); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(BackgroundTaskIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search }, - { _optionsStatus, model.Options.Status }, - }); + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var pagerShape = await _shapeFactory.PagerAsync(pager, taskItems.Count, routeData); - public async Task Edit(string name) + var model = new BackgroundTaskIndexViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) - { - return Forbid(); - } + Tasks = taskItems.OrderBy(entry => entry.Title).Skip(pager.GetStartIndex()).Take(pager.PageSize).ToList(), + Pager = pagerShape, + Options = options, + }; - var task = _backgroundTasks.GetTaskByName(name); - if (task == null) - { - return NotFound(); - } + return View(model); + } - var document = await _backgroundTaskManager.GetDocumentAsync(); + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(BackgroundTaskIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search }, + { _optionsStatus, model.Options.Status }, + }); - var defaultSettings = task.GetDefaultSettings(); - if (!document.Settings.TryGetValue(name, out var settings)) - { - settings = defaultSettings; - } + public async Task Edit(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) + { + return Forbid(); + } - var model = new BackgroundTaskViewModel - { - Name = defaultSettings.Name, - Title = defaultSettings.Title, - DefaultSchedule = defaultSettings.Schedule, - Schedule = settings.Schedule, - Description = settings.Description, - LockTimeout = settings.LockTimeout, - LockExpiration = settings.LockExpiration, - UsePipeline = settings.UsePipeline, - }; + var task = _backgroundTasks.GetTaskByName(name); + if (task == null) + { + return NotFound(); + } + + var document = await _backgroundTaskManager.GetDocumentAsync(); - return View(model); + var defaultSettings = task.GetDefaultSettings(); + if (!document.Settings.TryGetValue(name, out var settings)) + { + settings = defaultSettings; } - [HttpPost] - public async Task Edit(BackgroundTaskViewModel model) + var model = new BackgroundTaskViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) - { - return Forbid(); - } + Name = defaultSettings.Name, + Title = defaultSettings.Title, + DefaultSchedule = defaultSettings.Schedule, + Schedule = settings.Schedule, + Description = settings.Description, + LockTimeout = settings.LockTimeout, + LockExpiration = settings.LockExpiration, + UsePipeline = settings.UsePipeline, + }; + + return View(model); + } - var task = _backgroundTasks.GetTaskByName(model.Name); - if (task == null) - { - return NotFound(); - } + [HttpPost] + public async Task Edit(BackgroundTaskViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) + { + return Forbid(); + } - var defaultSettings = task.GetDefaultSettings(); + var task = _backgroundTasks.GetTaskByName(model.Name); + if (task == null) + { + return NotFound(); + } - if (ModelState.IsValid) + var defaultSettings = task.GetDefaultSettings(); + + if (ModelState.IsValid) + { + var document = await _backgroundTaskManager.LoadDocumentAsync(); + if (!document.Settings.TryGetValue(model.Name, out var settings)) { - var document = await _backgroundTaskManager.LoadDocumentAsync(); - if (!document.Settings.TryGetValue(model.Name, out var settings)) - { - settings = defaultSettings; - } + settings = defaultSettings; + } - settings.Title = defaultSettings.Title; - settings.Schedule = model.Schedule?.Trim(); - settings.Description = model.Description; - settings.LockTimeout = model.LockTimeout; - settings.LockExpiration = model.LockExpiration; - settings.UsePipeline = model.UsePipeline; + settings.Title = defaultSettings.Title; + settings.Schedule = model.Schedule?.Trim(); + settings.Description = model.Description; + settings.LockTimeout = model.LockTimeout; + settings.LockExpiration = model.LockExpiration; + settings.UsePipeline = model.UsePipeline; - await _backgroundTaskManager.UpdateAsync(model.Name, settings); + await _backgroundTaskManager.UpdateAsync(model.Name, settings); - await _notifier.SuccessAsync(H["The task has been updated."]); + await _notifier.SuccessAsync(H["The task has been updated."]); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); + } + + model.Title = defaultSettings.Title; + model.DefaultSchedule = defaultSettings.Schedule; - model.Title = defaultSettings.Title; - model.DefaultSchedule = defaultSettings.Schedule; + return View(model); + } - return View(model); + [HttpPost] + public async Task Enable(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) + { + return Forbid(); } - [HttpPost] - public async Task Enable(string name) + var task = _backgroundTasks.GetTaskByName(name); + if (task == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) - { - return Forbid(); - } + return NotFound(); + } - var task = _backgroundTasks.GetTaskByName(name); - if (task == null) - { - return NotFound(); - } + var document = await _backgroundTaskManager.LoadDocumentAsync(); + if (!document.Settings.TryGetValue(name, out var settings)) + { + settings = task.GetDefaultSettings(); + } - var document = await _backgroundTaskManager.LoadDocumentAsync(); - if (!document.Settings.TryGetValue(name, out var settings)) - { - settings = task.GetDefaultSettings(); - } + settings.Enable = true; - settings.Enable = true; + await _backgroundTaskManager.UpdateAsync(name, settings); - await _backgroundTaskManager.UpdateAsync(name, settings); + await _notifier.SuccessAsync(H["The task has been enabled."]); - await _notifier.SuccessAsync(H["The task has been enabled."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + [HttpPost] + public async Task Disable(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) + { + return Forbid(); } - [HttpPost] - public async Task Disable(string name) + var task = _backgroundTasks.GetTaskByName(name); + if (task == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageBackgroundTasks)) - { - return Forbid(); - } - - var task = _backgroundTasks.GetTaskByName(name); - if (task == null) - { - return NotFound(); - } + return NotFound(); + } - var document = await _backgroundTaskManager.LoadDocumentAsync(); - if (!document.Settings.TryGetValue(name, out var settings)) - { - settings = task.GetDefaultSettings(); - } + var document = await _backgroundTaskManager.LoadDocumentAsync(); + if (!document.Settings.TryGetValue(name, out var settings)) + { + settings = task.GetDefaultSettings(); + } - settings.Enable = false; + settings.Enable = false; - await _backgroundTaskManager.UpdateAsync(name, settings); + await _backgroundTaskManager.UpdateAsync(name, settings); - await _notifier.SuccessAsync(H["The task has been disabled."]); + await _notifier.SuccessAsync(H["The task has been disabled."]); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/AlwaysHasChangedToken.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/AlwaysHasChangedToken.cs index 72a683b5ade..f94da61df31 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/AlwaysHasChangedToken.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/AlwaysHasChangedToken.cs @@ -1,39 +1,38 @@ using System; using Microsoft.Extensions.Primitives; -namespace OrchardCore.BackgroundTasks.Services +namespace OrchardCore.BackgroundTasks.Services; + +/// +/// An empty change token that is always in the changed state but without raising any change callbacks. +/// +internal sealed class AlwaysHasChangedToken : IChangeToken { - /// - /// An empty change token that is always in the changed state but without raising any change callbacks. - /// - internal sealed class AlwaysHasChangedToken : IChangeToken - { - public static AlwaysHasChangedToken Singleton { get; } = new AlwaysHasChangedToken(); + public static AlwaysHasChangedToken Singleton { get; } = new AlwaysHasChangedToken(); - private AlwaysHasChangedToken() - { - } + private AlwaysHasChangedToken() + { + } - public bool HasChanged => true; + public bool HasChanged => true; - public bool ActiveChangeCallbacks => false; + public bool ActiveChangeCallbacks => false; - public IDisposable RegisterChangeCallback(Action callback, object state) - { - return EmptyDisposable.Instance; - } + public IDisposable RegisterChangeCallback(Action callback, object state) + { + return EmptyDisposable.Instance; } +} - internal sealed class EmptyDisposable : IDisposable - { - public static EmptyDisposable Instance { get; } = new EmptyDisposable(); +internal sealed class EmptyDisposable : IDisposable +{ + public static EmptyDisposable Instance { get; } = new EmptyDisposable(); - private EmptyDisposable() - { - } + private EmptyDisposable() + { + } - public void Dispose() - { - } + public void Dispose() + { } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskManager.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskManager.cs index ef882efbe39..1519cdfb23a 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskManager.cs @@ -3,45 +3,44 @@ using OrchardCore.Documents; using OrchardCore.Environment.Cache; -namespace OrchardCore.BackgroundTasks.Services +namespace OrchardCore.BackgroundTasks.Services; + +public class BackgroundTaskManager { - public class BackgroundTaskManager + private readonly IDocumentManager _documentManager; + private readonly ISignal _signal; + + public BackgroundTaskManager(IDocumentManager documentManager, ISignal signal) + { + _documentManager = documentManager; + _signal = signal; + } + + /// + /// Loads the background task document from the store for updating and that should not be cached. + /// + public Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); + + /// + /// Gets the background task document from the cache for sharing and that should not be updated. + /// + public Task GetDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); + + public async Task RemoveAsync(string name) + { + var document = await LoadDocumentAsync(); + document.Settings.Remove(name); + + await _documentManager.UpdateAsync(document); + _signal.DeferredSignalToken(nameof(BackgroundTaskSettings)); + } + + public async Task UpdateAsync(string name, BackgroundTaskSettings settings) { - private readonly IDocumentManager _documentManager; - private readonly ISignal _signal; - - public BackgroundTaskManager(IDocumentManager documentManager, ISignal signal) - { - _documentManager = documentManager; - _signal = signal; - } - - /// - /// Loads the background task document from the store for updating and that should not be cached. - /// - public Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); - - /// - /// Gets the background task document from the cache for sharing and that should not be updated. - /// - public Task GetDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); - - public async Task RemoveAsync(string name) - { - var document = await LoadDocumentAsync(); - document.Settings.Remove(name); - - await _documentManager.UpdateAsync(document); - _signal.DeferredSignalToken(nameof(BackgroundTaskSettings)); - } - - public async Task UpdateAsync(string name, BackgroundTaskSettings settings) - { - var document = await LoadDocumentAsync(); - document.Settings[name] = settings; - - await _documentManager.UpdateAsync(document); - _signal.DeferredSignalToken(nameof(BackgroundTaskSettings)); - } + var document = await LoadDocumentAsync(); + document.Settings[name] = settings; + + await _documentManager.UpdateAsync(document); + _signal.DeferredSignalToken(nameof(BackgroundTaskSettings)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskSettingsProvider.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskSettingsProvider.cs index 453965a7224..91056a8e555 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskSettingsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Services/BackgroundTaskSettingsProvider.cs @@ -2,31 +2,30 @@ using Microsoft.Extensions.Primitives; using OrchardCore.Environment.Cache; -namespace OrchardCore.BackgroundTasks.Services +namespace OrchardCore.BackgroundTasks.Services; + +public class BackgroundTaskSettingsProvider : IBackgroundTaskSettingsProvider { - public class BackgroundTaskSettingsProvider : IBackgroundTaskSettingsProvider + private readonly BackgroundTaskManager _backgroundTaskManager; + private readonly ISignal _signal; + + public BackgroundTaskSettingsProvider(BackgroundTaskManager backgroundTaskManager, ISignal signal) { - private readonly BackgroundTaskManager _backgroundTaskManager; - private readonly ISignal _signal; + _backgroundTaskManager = backgroundTaskManager; + _signal = signal; + } - public BackgroundTaskSettingsProvider(BackgroundTaskManager backgroundTaskManager, ISignal signal) - { - _backgroundTaskManager = backgroundTaskManager; - _signal = signal; - } + public IChangeToken ChangeToken => _signal.GetToken(nameof(BackgroundTaskSettings)); - public IChangeToken ChangeToken => _signal.GetToken(nameof(BackgroundTaskSettings)); + public async Task GetSettingsAsync(IBackgroundTask task) + { + var document = await _backgroundTaskManager.GetDocumentAsync(); - public async Task GetSettingsAsync(IBackgroundTask task) + if (document.Settings.TryGetValue(task.GetTaskName(), out var settings)) { - var document = await _backgroundTaskManager.GetDocumentAsync(); - - if (document.Settings.TryGetValue(task.GetTaskName(), out var settings)) - { - return settings; - } - - return null; + return settings; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Startup.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Startup.cs index 79f7a08dee7..65705a7a8ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/Startup.cs @@ -4,17 +4,16 @@ using OrchardCore.Navigation; using OrchardCore.Security.Permissions; -namespace OrchardCore.BackgroundTasks +namespace OrchardCore.BackgroundTasks; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped(); - } + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskIndexViewModel.cs index f2e5d52b5ee..1a4cc893358 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskIndexViewModel.cs @@ -1,27 +1,26 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.BackgroundTasks.ViewModels +namespace OrchardCore.BackgroundTasks.ViewModels; + +public class BackgroundTaskIndexViewModel { - public class BackgroundTaskIndexViewModel - { - public AdminIndexOptions Options { get; set; } + public AdminIndexOptions Options { get; set; } - [BindNever] - public IList Tasks { get; set; } + [BindNever] + public IList Tasks { get; set; } - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } +} - public class BackgroundTaskEntry - { - public string Name { get; set; } +public class BackgroundTaskEntry +{ + public string Name { get; set; } - public string Title { get; set; } + public string Title { get; set; } - public bool Enable { get; set; } + public bool Enable { get; set; } - public string Description { get; set; } - } + public string Description { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskViewModel.cs index 2250d3e5be9..bfb45d276af 100644 --- a/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.BackgroundTasks/ViewModels/BackgroundTaskViewModel.cs @@ -1,28 +1,27 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.BackgroundTasks.ViewModels +namespace OrchardCore.BackgroundTasks.ViewModels; + +public class BackgroundTaskViewModel { - public class BackgroundTaskViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public string Title { get; set; } + public string Title { get; set; } - [BindNever] - public string DefaultSchedule { get; set; } + [BindNever] + public string DefaultSchedule { get; set; } - public string Schedule { get; set; } + public string Schedule { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public bool Enable { get; set; } = true; + public bool Enable { get; set; } = true; - public int LockTimeout { get; set; } + public int LockTimeout { get; set; } - public int LockExpiration { get; set; } + public int LockExpiration { get; set; } - public bool UsePipeline { get; set; } + public bool UsePipeline { get; set; } - public bool IsAtomic => LockTimeout > 0 && LockExpiration > 0; - } + public bool IsAtomic => LockTimeout > 0 && LockExpiration > 0; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/ContentPickerAdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/ContentPickerAdminController.cs index 9f4fb5fe130..557f7618eab 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/ContentPickerAdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/ContentPickerAdminController.cs @@ -13,97 +13,96 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Contents; -namespace OrchardCore.ContentFields.Controllers +namespace OrchardCore.ContentFields.Controllers; + +[Admin] +public class ContentPickerAdminController : Controller { - [Admin] - public class ContentPickerAdminController : Controller + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IEnumerable _resultProviders; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + public ContentPickerAdminController( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IEnumerable resultProviders, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) + { + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _resultProviders = resultProviders; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } + + [Admin("ContentFields/SearchContentItems", "ContentPicker")] + public async Task SearchContentItems(string part, string field, string query) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IEnumerable _resultProviders; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - - public ContentPickerAdminController( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - IEnumerable resultProviders, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field)) { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _resultProviders = resultProviders; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; + return BadRequest("Part and field are required parameters"); } - [Admin("ContentFields/SearchContentItems", "ContentPicker")] - public async Task SearchContentItems(string part, string field, string query) + var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields + .FirstOrDefault(f => f.Name == field); + + var fieldSettings = partFieldDefinition?.GetSettings(); + if (fieldSettings == null) { - if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field)) - { - return BadRequest("Part and field are required parameters"); - } + return BadRequest("Unable to find field definition"); + } - var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields - .FirstOrDefault(f => f.Name == field); + var editor = partFieldDefinition.Editor() ?? "Default"; - var fieldSettings = partFieldDefinition?.GetSettings(); - if (fieldSettings == null) - { - return BadRequest("Unable to find field definition"); - } + var resultProvider = _resultProviders.FirstOrDefault(p => p.Name == editor) + ?? _resultProviders.FirstOrDefault(p => p.Name == "Default"); - var editor = partFieldDefinition.Editor() ?? "Default"; + if (resultProvider == null) + { + return new ObjectResult(new List()); + } - var resultProvider = _resultProviders.FirstOrDefault(p => p.Name == editor) - ?? _resultProviders.FirstOrDefault(p => p.Name == "Default"); + var contentTypes = fieldSettings.DisplayedContentTypes; - if (resultProvider == null) - { - return new ObjectResult(new List()); - } + if (fieldSettings.DisplayedStereotypes != null && fieldSettings.DisplayedStereotypes.Length > 0) + { + contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(contentType => + { + var hasStereotype = contentType.TryGetStereotype(out var stereotype); - var contentTypes = fieldSettings.DisplayedContentTypes; + return hasStereotype && fieldSettings.DisplayedStereotypes.Contains(stereotype); + }).Select(contentType => contentType.Name) + .ToArray(); + } - if (fieldSettings.DisplayedStereotypes != null && fieldSettings.DisplayedStereotypes.Length > 0) - { - contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(contentType => - { - var hasStereotype = contentType.TryGetStereotype(out var stereotype); + var results = await resultProvider.Search(new ContentPickerSearchContext + { + Query = query, + DisplayAllContentTypes = fieldSettings.DisplayAllContentTypes, + ContentTypes = contentTypes, + PartFieldDefinition = partFieldDefinition, + }); - return hasStereotype && fieldSettings.DisplayedStereotypes.Contains(stereotype); - }).Select(contentType => contentType.Name) - .ToArray(); - } + var contentItems = await _contentManager + .GetAsync(results.Select(r => r.ContentItemId)); - var results = await resultProvider.Search(new ContentPickerSearchContext + var selectedItems = new List(); + var user = _httpContextAccessor.HttpContext?.User; + foreach (var contentItem in contentItems) + { + selectedItems.Add(new VueMultiselectItemViewModel() { - Query = query, - DisplayAllContentTypes = fieldSettings.DisplayAllContentTypes, - ContentTypes = contentTypes, - PartFieldDefinition = partFieldDefinition, + Id = contentItem.ContentItemId, + DisplayText = contentItem.ToString(), + HasPublished = contentItem.IsPublished(), + IsViewable = await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, contentItem) }); - - var contentItems = await _contentManager - .GetAsync(results.Select(r => r.ContentItemId)); - - var selectedItems = new List(); - var user = _httpContextAccessor.HttpContext?.User; - foreach (var contentItem in contentItems) - { - selectedItems.Add(new VueMultiselectItemViewModel() - { - Id = contentItem.ContentItemId, - DisplayText = contentItem.ToString(), - HasPublished = contentItem.IsPublished(), - IsViewable = await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, contentItem) - }); - } - - return new ObjectResult(selectedItems); } + + return new ObjectResult(selectedItems); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs index 8e0f2101a2b..6650901c0af 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/LocalizationSetContentPickerAdminController.cs @@ -16,82 +16,81 @@ using YesSql.Services; using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor; -namespace OrchardCore.ContentFields.Controllers +namespace OrchardCore.ContentFields.Controllers; + +[RequireFeatures("OrchardCore.ContentLocalization")] +[Admin] +public class LocalizationSetContentPickerAdminController : Controller { - [RequireFeatures("OrchardCore.ContentLocalization")] - [Admin] - public class LocalizationSetContentPickerAdminController : Controller + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentLocalizationManager _contentLocalizationManager; + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + public LocalizationSetContentPickerAdminController( + IContentDefinitionManager contentDefinitionManager, + IContentLocalizationManager contentLocalizationManager, + IContentManager contentManager, + ISession session, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentLocalizationManager _contentLocalizationManager; - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; + _contentDefinitionManager = contentDefinitionManager; + _contentLocalizationManager = contentLocalizationManager; + _contentManager = contentManager; + _session = session; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } - public LocalizationSetContentPickerAdminController( - IContentDefinitionManager contentDefinitionManager, - IContentLocalizationManager contentLocalizationManager, - IContentManager contentManager, - ISession session, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + [HttpGet] + [Admin("ContentFields/SearchLocalizationSets", "SearchLocalizationSets")] + public async Task SearchLocalizationSets(string part, string field, string query) + { + if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field)) { - _contentDefinitionManager = contentDefinitionManager; - _contentLocalizationManager = contentLocalizationManager; - _contentManager = contentManager; - _session = session; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; + return BadRequest("Part and field are required parameters"); } - [HttpGet] - [Admin("ContentFields/SearchLocalizationSets", "SearchLocalizationSets")] - public async Task SearchLocalizationSets(string part, string field, string query) - { - if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field)) - { - return BadRequest("Part and field are required parameters"); - } - - var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields - .FirstOrDefault(f => f.Name == field); + var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields + .FirstOrDefault(f => f.Name == field); - var fieldSettings = partFieldDefinition?.GetSettings(); - if (fieldSettings == null) - { - return BadRequest("Unable to find field definition"); - } + var fieldSettings = partFieldDefinition?.GetSettings(); + if (fieldSettings == null) + { + return BadRequest("Unable to find field definition"); + } - var dbQuery = _session.Query() - .With(x => x.ContentType.IsIn(fieldSettings.DisplayedContentTypes) && x.Latest); + var dbQuery = _session.Query() + .With(x => x.ContentType.IsIn(fieldSettings.DisplayedContentTypes) && x.Latest); - if (!string.IsNullOrEmpty(query)) - { - dbQuery.With(x => x.DisplayText.Contains(query) || x.ContentType.Contains(query)); - } + if (!string.IsNullOrEmpty(query)) + { + dbQuery.With(x => x.DisplayText.Contains(query) || x.ContentType.Contains(query)); + } - var contentItems = await dbQuery.Take(40).ListAsync(); + var contentItems = await dbQuery.Take(40).ListAsync(); - // if 2 search results have the same set, select one based on the current culture - var cleanedContentItems = await _contentLocalizationManager.DeduplicateContentItemsAsync(contentItems); + // if 2 search results have the same set, select one based on the current culture + var cleanedContentItems = await _contentLocalizationManager.DeduplicateContentItemsAsync(contentItems); - var results = new List(); + var results = new List(); - foreach (var contentItem in cleanedContentItems) + foreach (var contentItem in cleanedContentItems) + { + if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) { - if (await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) + results.Add(new VueMultiselectItemViewModel { - results.Add(new VueMultiselectItemViewModel - { - Id = contentItem.Key, // localization set - DisplayText = contentItem.Value.ToString(), - HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value) - }); - } + Id = contentItem.Key, // localization set + DisplayText = contentItem.Value.ToString(), + HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem.Value) + }); } - - return new ObjectResult(results); } + + return new ObjectResult(results); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/UserPickerAdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/UserPickerAdminController.cs index 4107e4e3246..834339147a9 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/UserPickerAdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Controllers/UserPickerAdminController.cs @@ -13,74 +13,73 @@ using OrchardCore.Contents; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Controllers +namespace OrchardCore.ContentFields.Controllers; + +[RequireFeatures(OrchardCore.Users.UserConstants.Features.Users)] +[Admin] +public class UserPickerAdminController : Controller { - [RequireFeatures(OrchardCore.Users.UserConstants.Features.Users)] - [Admin] - public class UserPickerAdminController : Controller + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IAuthorizationService _authorizationService; + private readonly IEnumerable _resultProviders; + + public UserPickerAdminController( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IAuthorizationService authorizationService, + IEnumerable resultProviders + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IAuthorizationService _authorizationService; - private readonly IEnumerable _resultProviders; + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _authorizationService = authorizationService; + _resultProviders = resultProviders; + } - public UserPickerAdminController( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - IAuthorizationService authorizationService, - IEnumerable resultProviders - ) + [Admin("ContentFields/SearchUsers", "SearchUsers")] + public async Task SearchUsers(string part, string field, string contentType, string query) + { + if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field) || string.IsNullOrWhiteSpace(contentType)) { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _authorizationService = authorizationService; - _resultProviders = resultProviders; + return BadRequest("Part, field and contentType are required parameters"); } - [Admin("ContentFields/SearchUsers", "SearchUsers")] - public async Task SearchUsers(string part, string field, string contentType, string query) - { - if (string.IsNullOrWhiteSpace(part) || string.IsNullOrWhiteSpace(field) || string.IsNullOrWhiteSpace(contentType)) - { - return BadRequest("Part, field and contentType are required parameters"); - } - - var contentItem = await _contentManager.NewAsync(contentType); - contentItem.Owner = User.FindFirstValue(ClaimTypes.NameIdentifier); + var contentItem = await _contentManager.NewAsync(contentType); + contentItem.Owner = User.FindFirstValue(ClaimTypes.NameIdentifier); - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); + } - var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields - .FirstOrDefault(f => f.Name == field); + var partFieldDefinition = (await _contentDefinitionManager.GetPartDefinitionAsync(part))?.Fields + .FirstOrDefault(f => f.Name == field); - var fieldSettings = partFieldDefinition?.GetSettings(); - if (fieldSettings == null) - { - return BadRequest("Unable to find field definition"); - } + var fieldSettings = partFieldDefinition?.GetSettings(); + if (fieldSettings == null) + { + return BadRequest("Unable to find field definition"); + } - var editor = partFieldDefinition.Editor() ?? "Default"; + var editor = partFieldDefinition.Editor() ?? "Default"; - var resultProvider = _resultProviders.FirstOrDefault(p => p.Name == editor) - ?? _resultProviders.FirstOrDefault(p => p.Name == "Default"); + var resultProvider = _resultProviders.FirstOrDefault(p => p.Name == editor) + ?? _resultProviders.FirstOrDefault(p => p.Name == "Default"); - if (resultProvider == null) - { - return new ObjectResult(new List()); - } + if (resultProvider == null) + { + return new ObjectResult(new List()); + } - var results = await resultProvider.Search(new UserPickerSearchContext - { - Query = query, - DisplayAllUsers = fieldSettings.DisplayAllUsers, - Roles = fieldSettings.DisplayedRoles, - PartFieldDefinition = partFieldDefinition - }); + var results = await resultProvider.Search(new UserPickerSearchContext + { + Query = query, + DisplayAllUsers = fieldSettings.DisplayAllUsers, + Roles = fieldSettings.DisplayedRoles, + PartFieldDefinition = partFieldDefinition + }); - return new ObjectResult(results.Select(r => new VueMultiselectUserViewModel() { Id = r.UserId, DisplayText = r.DisplayText, IsEnabled = r.IsEnabled })); - } + return new ObjectResult(results.Select(r => new VueMultiselectUserViewModel() { Id = r.UserId, DisplayText = r.DisplayText, IsEnabled = r.IsEnabled })); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/BooleanFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/BooleanFieldDisplayDriver.cs index b7459fa82f0..686d7e923f7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/BooleanFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/BooleanFieldDisplayDriver.cs @@ -7,40 +7,39 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class BooleanFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class BooleanFieldDisplayDriver : ContentFieldDisplayDriver + public override IDisplayResult Display(BooleanField field, BuildFieldDisplayContext context) { - public override IDisplayResult Display(BooleanField field, BuildFieldDisplayContext context) + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(BooleanField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(BooleanField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetEditorShapeType(context), model => - { - model.Value = (context.IsNew == false) ? - field.Value : context.PartFieldDefinition.GetSettings().DefaultValue; + model.Value = (context.IsNew == false) ? + field.Value : context.PartFieldDefinition.GetSettings().DefaultValue; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override async Task UpdateAsync(BooleanField field, UpdateFieldEditorContext context) - { - await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); + public override async Task UpdateAsync(BooleanField field, UpdateFieldEditorContext context) + { + await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); - return Edit(field, context); - } + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/ContentPickerFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/ContentPickerFieldDisplayDriver.cs index f2ff68536fe..0697696e050 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/ContentPickerFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/ContentPickerFieldDisplayDriver.cs @@ -21,112 +21,111 @@ using OrchardCore.Localization; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class ContentPickerFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class ContentPickerFieldDisplayDriver : ContentFieldDisplayDriver + private readonly IContentManager _contentManager; + private readonly ILiquidTemplateManager _templateManager; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + internal readonly IStringLocalizer S; + + public ContentPickerFieldDisplayDriver( + IContentManager contentManager, + IStringLocalizer localizer, + ILiquidTemplateManager templateManager, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IContentManager _contentManager; - private readonly ILiquidTemplateManager _templateManager; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - - internal readonly IStringLocalizer S; - - public ContentPickerFieldDisplayDriver( - IContentManager contentManager, - IStringLocalizer localizer, - ILiquidTemplateManager templateManager, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) - { - _contentManager = contentManager; - S = localizer; - _templateManager = templateManager; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - } + _contentManager = contentManager; + S = localizer; + _templateManager = templateManager; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } - public override IDisplayResult Display(ContentPickerField field, BuildFieldDisplayContext fieldDisplayContext) + public override IDisplayResult Display(ContentPickerField field, BuildFieldDisplayContext fieldDisplayContext) + { + return Initialize(GetDisplayShapeType(fieldDisplayContext), model => { - return Initialize(GetDisplayShapeType(fieldDisplayContext), model => - { - model.Field = field; - model.Part = fieldDisplayContext.ContentPart; - model.PartFieldDefinition = fieldDisplayContext.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = fieldDisplayContext.ContentPart; + model.PartFieldDefinition = fieldDisplayContext.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(ContentPickerField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(ContentPickerField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - model.ContentItemIds = string.Join(",", field.ContentItemIds); + model.ContentItemIds = string.Join(",", field.ContentItemIds); - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; - model.SelectedItems = []; - var settings = context.PartFieldDefinition.GetSettings(); + model.SelectedItems = []; + var settings = context.PartFieldDefinition.GetSettings(); - foreach (var contentItemId in field.ContentItemIds) - { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + foreach (var contentItemId in field.ContentItemIds) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - if (contentItem == null) - { - continue; - } + if (contentItem == null) + { + continue; + } - var cultureAspect = await _contentManager.PopulateAspectAsync(contentItem, new CultureAspect()); + var cultureAspect = await _contentManager.PopulateAspectAsync(contentItem, new CultureAspect()); - using (CultureScope.Create(cultureAspect.Culture)) + using (CultureScope.Create(cultureAspect.Culture)) + { + model.SelectedItems.Add(new VueMultiselectItemViewModel { - model.SelectedItems.Add(new VueMultiselectItemViewModel - { - Id = contentItemId, - DisplayText = await _templateManager.RenderStringAsync(settings.TitlePattern, NullEncoder.Default, contentItem, - new Dictionary() { [nameof(ContentItem)] = new ObjectValue(contentItem) }), - HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem), - IsEditable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.EditContent, contentItem), - IsViewable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.ViewContent, contentItem) - }); - } - + Id = contentItemId, + DisplayText = await _templateManager.RenderStringAsync(settings.TitlePattern, NullEncoder.Default, contentItem, + new Dictionary() { [nameof(ContentItem)] = new ObjectValue(contentItem) }), + HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem), + IsEditable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.EditContent, contentItem), + IsViewable = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext!.User, CommonPermissions.ViewContent, contentItem) + }); } - }); - } - public override async Task UpdateAsync(ContentPickerField field, UpdateFieldEditorContext context) - { - var viewModel = new EditContentPickerFieldViewModel(); + } + }); + } - var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.ContentItemIds); + public override async Task UpdateAsync(ContentPickerField field, UpdateFieldEditorContext context) + { + var viewModel = new EditContentPickerFieldViewModel(); - if (!modelUpdated) - { - return Edit(field, context); - } + var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.ContentItemIds); - field.ContentItemIds = viewModel.ContentItemIds == null - ? [] - : viewModel.ContentItemIds.Split(',', StringSplitOptions.RemoveEmptyEntries); + if (!modelUpdated) + { + return Edit(field, context); + } - var settings = context.PartFieldDefinition.GetSettings(); + field.ContentItemIds = viewModel.ContentItemIds == null + ? [] + : viewModel.ContentItemIds.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (settings.Required && field.ContentItemIds.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.ContentItemIds), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); - } + var settings = context.PartFieldDefinition.GetSettings(); - if (!settings.Multiple && field.ContentItemIds.Length > 1) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.ContentItemIds), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); - } + if (settings.Required && field.ContentItemIds.Length == 0) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.ContentItemIds), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); + } - return Edit(field, context); + if (!settings.Multiple && field.ContentItemIds.Length > 1) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.ContentItemIds), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateFieldDisplayDriver.cs index 566ba0c2df6..a7f5f5639f5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateFieldDisplayDriver.cs @@ -10,51 +10,50 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class DateFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class DateFieldDisplayDriver : ContentFieldDisplayDriver + internal readonly IStringLocalizer S; + + public DateFieldDisplayDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public DateFieldDisplayDriver(IStringLocalizer localizer) + public override IDisplayResult Display(DateField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - S = localizer; - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Display(DateField field, BuildFieldDisplayContext context) + public override IDisplayResult Edit(DateField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Value = field.Value; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override IDisplayResult Edit(DateField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => - { - model.Value = field.Value; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + public override async Task UpdateAsync(DateField field, UpdateFieldEditorContext context) + { + await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); + var settings = context.PartFieldDefinition.GetSettings(); - public override async Task UpdateAsync(DateField field, UpdateFieldEditorContext context) + if (settings.Required && field.Value == null) { - await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); - var settings = context.PartFieldDefinition.GetSettings(); - - if (settings.Required && field.Value == null) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } - - return Edit(field, context); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateTimeFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateTimeFieldDisplayDriver.cs index d591dc2335f..38a618a37ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateTimeFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/DateTimeFieldDisplayDriver.cs @@ -12,70 +12,69 @@ using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class DateTimeFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class DateTimeFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly ILocalClock _localClock; + private readonly ILocalClock _localClock; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public DateTimeFieldDisplayDriver( - ILocalClock localClock, - IStringLocalizer localizer) - { - _localClock = localClock; - S = localizer; - } + public DateTimeFieldDisplayDriver( + ILocalClock localClock, + IStringLocalizer localizer) + { + _localClock = localClock; + S = localizer; + } - public override IDisplayResult Display(DateTimeField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(DateTimeField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), async model => { - return Initialize(GetDisplayShapeType(context), async model => - { - model.LocalDateTime = field.Value == null ? null : (await _localClock.ConvertToLocalAsync(field.Value.Value)).DateTime; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.LocalDateTime = field.Value == null ? null : (await _localClock.ConvertToLocalAsync(field.Value.Value)).DateTime; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(DateTimeField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(DateTimeField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - model.LocalDateTime = field.Value == null ? null : (await _localClock.ConvertToLocalAsync(field.Value.Value)).DateTime; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + model.LocalDateTime = field.Value == null ? null : (await _localClock.ConvertToLocalAsync(field.Value.Value)).DateTime; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override async Task UpdateAsync(DateTimeField field, UpdateFieldEditorContext context) - { - var model = new EditDateTimeFieldViewModel(); + public override async Task UpdateAsync(DateTimeField field, UpdateFieldEditorContext context) + { + var model = new EditDateTimeFieldViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.LocalDateTime); - var settings = context.PartFieldDefinition.GetSettings(); + await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.LocalDateTime); + var settings = context.PartFieldDefinition.GetSettings(); - if (settings.Required && model.LocalDateTime == null) + if (settings.Required && model.LocalDateTime == null) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.LocalDateTime), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } + else + { + if (model.LocalDateTime == null) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.LocalDateTime), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + field.Value = null; } else { - if (model.LocalDateTime == null) - { - field.Value = null; - } - else - { - field.Value = await _localClock.ConvertToUtcAsync(model.LocalDateTime.Value); - } + field.Value = await _localClock.ConvertToUtcAsync(model.LocalDateTime.Value); } - - return Edit(field, context); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/HtmlFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/HtmlFieldDisplayDriver.cs index 0dbc44efaf9..09e3782c5ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/HtmlFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/HtmlFieldDisplayDriver.cs @@ -17,92 +17,91 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class HtmlFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class HtmlFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly IShortcodeService _shortcodeService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly IShortcodeService _shortcodeService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public HtmlFieldDisplayDriver( - ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder, - IHtmlSanitizerService htmlSanitizerService, - IShortcodeService shortcodeService, - IStringLocalizer localizer) - { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - _htmlSanitizerService = htmlSanitizerService; - _shortcodeService = shortcodeService; - S = localizer; - } + public HtmlFieldDisplayDriver( + ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder, + IHtmlSanitizerService htmlSanitizerService, + IShortcodeService shortcodeService, + IStringLocalizer localizer) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + _htmlSanitizerService = htmlSanitizerService; + _shortcodeService = shortcodeService; + S = localizer; + } - public override IDisplayResult Display(HtmlField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(HtmlField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), async model => { - return Initialize(GetDisplayShapeType(context), async model => + model.Html = field.Html; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + + var settings = context.PartFieldDefinition.GetSettings(); + if (!settings.SanitizeHtml) { - model.Html = field.Html; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; + model.Html = await _liquidTemplateManager.RenderStringAsync(field.Html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(field.ContentItem) }); + } - var settings = context.PartFieldDefinition.GetSettings(); - if (!settings.SanitizeHtml) + model.Html = await _shortcodeService.ProcessAsync(model.Html, + new Context { - model.Html = await _liquidTemplateManager.RenderStringAsync(field.Html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(field.ContentItem) }); - } + ["ContentItem"] = field.ContentItem, + ["PartFieldDefinition"] = context.PartFieldDefinition + }); - model.Html = await _shortcodeService.ProcessAsync(model.Html, - new Context - { - ["ContentItem"] = field.ContentItem, - ["PartFieldDefinition"] = context.PartFieldDefinition - }); - - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } - - public override IDisplayResult Edit(HtmlField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => - { - model.Html = field.Html; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override async Task UpdateAsync(HtmlField field, UpdateFieldEditorContext context) + public override IDisplayResult Edit(HtmlField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - var viewModel = new EditHtmlFieldViewModel(); + model.Html = field.Html; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - var settings = context.PartFieldDefinition.GetSettings(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Html); + public override async Task UpdateAsync(HtmlField field, UpdateFieldEditorContext context) + { + var viewModel = new EditHtmlFieldViewModel(); - if (!string.IsNullOrEmpty(viewModel.Html) && !_liquidTemplateManager.Validate(viewModel.Html, out var errors)) - { - var fieldName = context.PartFieldDefinition.DisplayName(); - context.Updater.ModelState.AddModelError( - Prefix, - nameof(viewModel.Html), S["{0} doesn't contain a valid Liquid expression. Details: {1}", - fieldName, - string.Join(' ', errors)]); - } - else - { - field.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(viewModel.Html) : viewModel.Html; - } + var settings = context.PartFieldDefinition.GetSettings(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Html); - return Edit(field, context); + if (!string.IsNullOrEmpty(viewModel.Html) && !_liquidTemplateManager.Validate(viewModel.Html, out var errors)) + { + var fieldName = context.PartFieldDefinition.DisplayName(); + context.Updater.ModelState.AddModelError( + Prefix, + nameof(viewModel.Html), S["{0} doesn't contain a valid Liquid expression. Details: {1}", + fieldName, + string.Join(' ', errors)]); } + else + { + field.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(viewModel.Html) : viewModel.Html; + } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LinkFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LinkFieldDisplayDriver.cs index abd44de36c6..f4e8cbdfd79 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LinkFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LinkFieldDisplayDriver.cs @@ -15,118 +15,117 @@ using OrchardCore.Infrastructure.Html; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class LinkFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class LinkFieldDisplayDriver : ContentFieldDisplayDriver + private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly HtmlEncoder _htmlencoder; + + internal readonly IStringLocalizer S; + + public LinkFieldDisplayDriver( + IUrlHelperFactory urlHelperFactory, + IActionContextAccessor actionContextAccessor, + IStringLocalizer localizer, + IHtmlSanitizerService htmlSanitizerService, + HtmlEncoder htmlencoder) { - private readonly IUrlHelperFactory _urlHelperFactory; - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly HtmlEncoder _htmlencoder; - - internal readonly IStringLocalizer S; - - public LinkFieldDisplayDriver( - IUrlHelperFactory urlHelperFactory, - IActionContextAccessor actionContextAccessor, - IStringLocalizer localizer, - IHtmlSanitizerService htmlSanitizerService, - HtmlEncoder htmlencoder) - { - _urlHelperFactory = urlHelperFactory; - _actionContextAccessor = actionContextAccessor; - S = localizer; - _htmlSanitizerService = htmlSanitizerService; - _htmlencoder = htmlencoder; - } + _urlHelperFactory = urlHelperFactory; + _actionContextAccessor = actionContextAccessor; + S = localizer; + _htmlSanitizerService = htmlSanitizerService; + _htmlencoder = htmlencoder; + } - public override IDisplayResult Display(LinkField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(LinkField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(LinkField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(LinkField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetEditorShapeType(context), model => - { - var settings = context.PartFieldDefinition.GetSettings(); - model.Url = context.IsNew && field.Url == null ? settings.DefaultUrl : field.Url; - model.Text = context.IsNew && field.Text == null ? settings.DefaultText : field.Text; - model.Target = context.IsNew && field.Target == null ? settings.DefaultTarget : field.Target; - - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + var settings = context.PartFieldDefinition.GetSettings(); + model.Url = context.IsNew && field.Url == null ? settings.DefaultUrl : field.Url; + model.Text = context.IsNew && field.Text == null ? settings.DefaultText : field.Text; + model.Target = context.IsNew && field.Target == null ? settings.DefaultTarget : field.Target; + + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } + + public override async Task UpdateAsync(LinkField field, UpdateFieldEditorContext context) + { + var modelUpdated = await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Url, f => f.Text, f => f.Target); - public override async Task UpdateAsync(LinkField field, UpdateFieldEditorContext context) + if (modelUpdated) { - var modelUpdated = await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Url, f => f.Text, f => f.Target); + var settings = context.PartFieldDefinition.GetSettings(); - if (modelUpdated) + var urlToValidate = field.Url; + if (!string.IsNullOrEmpty(urlToValidate)) { - var settings = context.PartFieldDefinition.GetSettings(); - - var urlToValidate = field.Url; - if (!string.IsNullOrEmpty(urlToValidate)) + var indexAnchor = urlToValidate.IndexOf('#'); + if (indexAnchor > -1) { - var indexAnchor = urlToValidate.IndexOf('#'); - if (indexAnchor > -1) - { - urlToValidate = urlToValidate[..indexAnchor]; - } - - if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) - { - var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); - urlToValidate = urlHelper.Content(urlToValidate); - } - - urlToValidate = urlToValidate.ToUriComponents(); + urlToValidate = urlToValidate[..indexAnchor]; } - // Validate Url - if (settings.Required && string.IsNullOrWhiteSpace(field.Url)) + if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["The url is required for {0}.", context.PartFieldDefinition.DisplayName()]); + var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); + urlToValidate = urlHelper.Content(urlToValidate); } - else if (!string.IsNullOrWhiteSpace(field.Url)) - { - if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["{0} is an invalid url.", field.Url]); - } - else - { - var link = $""; - if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["{0} is an invalid url.", field.Url]); - } - } - } + urlToValidate = urlToValidate.ToUriComponents(); + } - // Validate Text - if (settings.LinkTextMode == LinkTextMode.Required && string.IsNullOrWhiteSpace(field.Text)) + // Validate Url + if (settings.Required && string.IsNullOrWhiteSpace(field.Url)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["The url is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } + else if (!string.IsNullOrWhiteSpace(field.Url)) + { + if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["The link text is required for {0}.", context.PartFieldDefinition.DisplayName()]); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["{0} is an invalid url.", field.Url]); } - else if (settings.LinkTextMode == LinkTextMode.Static && string.IsNullOrWhiteSpace(settings.DefaultText)) + else { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["The text default value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + var link = $""; + + if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Url), S["{0} is an invalid url.", field.Url]); + } } } - return Edit(field, context); + // Validate Text + if (settings.LinkTextMode == LinkTextMode.Required && string.IsNullOrWhiteSpace(field.Text)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["The link text is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } + else if (settings.LinkTextMode == LinkTextMode.Static && string.IsNullOrWhiteSpace(settings.DefaultText)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["The text default value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LocalizationSetContentPickerFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LocalizationSetContentPickerFieldDisplayDriver.cs index ed9cf0f359c..185b493796f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LocalizationSetContentPickerFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/LocalizationSetContentPickerFieldDisplayDriver.cs @@ -14,96 +14,95 @@ using OrchardCore.Modules; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +[RequireFeatures("OrchardCore.ContentLocalization")] +public sealed class LocalizationSetContentPickerFieldDisplayDriver : ContentFieldDisplayDriver { - [RequireFeatures("OrchardCore.ContentLocalization")] - public sealed class LocalizationSetContentPickerFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly IContentManager _contentManager; - private readonly IContentLocalizationManager _contentLocalizationManager; + private readonly IContentManager _contentManager; + private readonly IContentLocalizationManager _contentLocalizationManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public LocalizationSetContentPickerFieldDisplayDriver( - IContentManager contentManager, - IStringLocalizer localizer, - IContentLocalizationManager contentLocalizationManager) - { - _contentManager = contentManager; - S = localizer; - _contentLocalizationManager = contentLocalizationManager; - } + public LocalizationSetContentPickerFieldDisplayDriver( + IContentManager contentManager, + IStringLocalizer localizer, + IContentLocalizationManager contentLocalizationManager) + { + _contentManager = contentManager; + S = localizer; + _contentLocalizationManager = contentLocalizationManager; + } - public override IDisplayResult Display(LocalizationSetContentPickerField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(LocalizationSetContentPickerField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(LocalizationSetContentPickerField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(LocalizationSetContentPickerField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - model.LocalizationSets = string.Join(",", field.LocalizationSets); + model.LocalizationSets = string.Join(",", field.LocalizationSets); + + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; + model.SelectedItems = []; - model.SelectedItems = []; + foreach (var kvp in await _contentLocalizationManager.GetFirstItemIdForSetsAsync(field.LocalizationSets)) + { + var contentItem = await _contentManager.GetAsync(kvp.Value, VersionOptions.Latest); - foreach (var kvp in await _contentLocalizationManager.GetFirstItemIdForSetsAsync(field.LocalizationSets)) + if (contentItem == null) { - var contentItem = await _contentManager.GetAsync(kvp.Value, VersionOptions.Latest); - - if (contentItem == null) - { - continue; - } - - model.SelectedItems.Add(new VueMultiselectItemViewModel - { - Id = kvp.Key, // localization set - DisplayText = contentItem.ToString(), - HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem) - }); + continue; } - }); - } - public override async Task UpdateAsync(LocalizationSetContentPickerField field, UpdateFieldEditorContext context) - { - var viewModel = new EditLocalizationSetContentPickerFieldViewModel(); + model.SelectedItems.Add(new VueMultiselectItemViewModel + { + Id = kvp.Key, // localization set + DisplayText = contentItem.ToString(), + HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem) + }); + } + }); + } - var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.LocalizationSets); + public override async Task UpdateAsync(LocalizationSetContentPickerField field, UpdateFieldEditorContext context) + { + var viewModel = new EditLocalizationSetContentPickerFieldViewModel(); - if (!modelUpdated) - { - return Edit(field, context); - } + var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.LocalizationSets); - field.LocalizationSets = viewModel.LocalizationSets == null - ? [] : viewModel.LocalizationSets.Split(',', StringSplitOptions.RemoveEmptyEntries); + if (!modelUpdated) + { + return Edit(field, context); + } - var settings = context.PartFieldDefinition.GetSettings(); + field.LocalizationSets = viewModel.LocalizationSets == null + ? [] : viewModel.LocalizationSets.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (settings.Required && field.LocalizationSets.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.LocalizationSets), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); - } + var settings = context.PartFieldDefinition.GetSettings(); - if (!settings.Multiple && field.LocalizationSets.Length > 1) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.LocalizationSets), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); - } + if (settings.Required && field.LocalizationSets.Length == 0) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.LocalizationSets), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); + } - return Edit(field, context); + if (!settings.Multiple && field.LocalizationSets.Length > 1) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.LocalizationSets), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/MultiTextFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/MultiTextFieldDisplayDriver.cs index 0a488a890af..3ca02ac3e04 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/MultiTextFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/MultiTextFieldDisplayDriver.cs @@ -11,65 +11,64 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public sealed class MultiTextFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class MultiTextFieldDisplayDriver : ContentFieldDisplayDriver + internal readonly IStringLocalizer S; + + public MultiTextFieldDisplayDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public MultiTextFieldDisplayDriver(IStringLocalizer localizer) + public override IDisplayResult Display(MultiTextField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - S = localizer; - } + var settings = context.PartFieldDefinition.GetSettings(); - public override IDisplayResult Display(MultiTextField field, BuildFieldDisplayContext context) + model.Values = settings.Options.Where(o => field.Values?.Contains(o.Value) == true).Select(o => o.Value).ToArray(); + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } + + public override IDisplayResult Edit(MultiTextField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => + if (context.IsNew) { var settings = context.PartFieldDefinition.GetSettings(); - - model.Values = settings.Options.Where(o => field.Values?.Contains(o.Value) == true).Select(o => o.Value).ToArray(); - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } - - public override IDisplayResult Edit(MultiTextField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => + model.Values = settings.Options.Where(o => o.Default).Select(o => o.Value).ToArray(); + } + else { - if (context.IsNew) - { - var settings = context.PartFieldDefinition.GetSettings(); - model.Values = settings.Options.Where(o => o.Default).Select(o => o.Value).ToArray(); - } - else - { - model.Values = field.Values; - } - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } - - public override async Task UpdateAsync(MultiTextField field, UpdateFieldEditorContext context) - { - var viewModel = new EditMultiTextFieldViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + model.Values = field.Values; + } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - field.Values = viewModel.Values; + public override async Task UpdateAsync(MultiTextField field, UpdateFieldEditorContext context) + { + var viewModel = new EditMultiTextFieldViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - var settings = context.PartFieldDefinition.GetSettings(); - if (settings.Required && viewModel.Values.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Values), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + field.Values = viewModel.Values; - return Edit(field, context); + var settings = context.PartFieldDefinition.GetSettings(); + if (settings.Required && viewModel.Values.Length == 0) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Values), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/NumericFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/NumericFieldDisplayDriver.cs index 5fe649fb873..7ddff407e23 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/NumericFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/NumericFieldDisplayDriver.cs @@ -11,106 +11,105 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class NumericFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class NumericFieldDisplayDriver : ContentFieldDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public NumericFieldDisplayDriver(IStringLocalizer localizer) - { - S = localizer; - } + public NumericFieldDisplayDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Display(NumericField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(NumericField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(NumericField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(NumericField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetEditorShapeType(context), model => - { - var settings = context.PartFieldDefinition.GetSettings(); + var settings = context.PartFieldDefinition.GetSettings(); - // The default value of a field is intended for the editor when a new content item - // is created (not for APIs). Since we may want to render the editor of a content - // item that was created by code, we only set the default value in the - // of the field if it doesn't already have a value. + // The default value of a field is intended for the editor when a new content item + // is created (not for APIs). Since we may want to render the editor of a content + // item that was created by code, we only set the default value in the + // of the field if it doesn't already have a value. - if (field.Value.HasValue) - { - model.Value = Convert.ToString(field.Value, CultureInfo.CurrentUICulture); - } - else if (context.IsNew) - { - // The content item is new and the field is not initialized, we can - // use the default value from the settings in the editor. - model.Value = settings.DefaultValue; - } + if (field.Value.HasValue) + { + model.Value = Convert.ToString(field.Value, CultureInfo.CurrentUICulture); + } + else if (context.IsNew) + { + // The content item is new and the field is not initialized, we can + // use the default value from the settings in the editor. + model.Value = settings.DefaultValue; + } - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override async Task UpdateAsync(NumericField field, UpdateFieldEditorContext context) - { - var viewModel = new EditNumericFieldViewModel(); + public override async Task UpdateAsync(NumericField field, UpdateFieldEditorContext context) + { + var viewModel = new EditNumericFieldViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Value); - var settings = context.PartFieldDefinition.GetSettings(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Value); + var settings = context.PartFieldDefinition.GetSettings(); - field.Value = null; + field.Value = null; - if (string.IsNullOrWhiteSpace(viewModel.Value)) + if (string.IsNullOrWhiteSpace(viewModel.Value)) + { + if (settings.Required) { - if (settings.Required) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } - else if (!decimal.TryParse(viewModel.Value, NumberStyles.Any, CultureInfo.CurrentUICulture, out var value)) + } + else if (!decimal.TryParse(viewModel.Value, NumberStyles.Any, CultureInfo.CurrentUICulture, out var value)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + } + else + { + field.Value = value; + + if (settings.Minimum.HasValue && value < settings.Minimum.Value) { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The value must be greater than {0}.", settings.Minimum.Value]); } - else - { - field.Value = value; - if (settings.Minimum.HasValue && value < settings.Minimum.Value) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The value must be greater than {0}.", settings.Minimum.Value]); - } + if (settings.Maximum.HasValue && value > settings.Maximum.Value) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The value must be less than {0}.", settings.Maximum.Value]); + } - if (settings.Maximum.HasValue && value > settings.Maximum.Value) + // Check the number of decimals. + if (Math.Round(value, settings.Scale) != value) + { + if (settings.Scale == 0) { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The value must be less than {0}.", settings.Maximum.Value]); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The {0} field must be an integer.", context.PartFieldDefinition.DisplayName()]); } - - // Check the number of decimals. - if (Math.Round(value, settings.Scale) != value) + else { - if (settings.Scale == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["The {0} field must be an integer.", context.PartFieldDefinition.DisplayName()]); - } - else - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["Invalid number of digits for {0}, max allowed: {1}.", context.PartFieldDefinition.DisplayName(), settings.Scale]); - } + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["Invalid number of digits for {0}, max allowed: {1}.", context.PartFieldDefinition.DisplayName(), settings.Scale]); } } - - return Edit(field, context); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TextFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TextFieldDisplayDriver.cs index 7c34fd37afa..6bdfc67d3e2 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TextFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TextFieldDisplayDriver.cs @@ -11,52 +11,51 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers -{ - public sealed class TextFieldDisplayDriver : ContentFieldDisplayDriver - { - internal readonly IStringLocalizer S; +namespace OrchardCore.ContentFields.Drivers; - public TextFieldDisplayDriver(IStringLocalizer localizer) - { - S = localizer; - } +public sealed class TextFieldDisplayDriver : ContentFieldDisplayDriver +{ + internal readonly IStringLocalizer S; - public override IDisplayResult Display(TextField field, BuildFieldDisplayContext context) - { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + public TextFieldDisplayDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Edit(TextField field, BuildFieldEditorContext context) + public override IDisplayResult Display(TextField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetEditorShapeType(context), model => - { - var settings = context.PartFieldDefinition.GetSettings(); - model.Text = context.IsNew && field.Text == null ? settings.DefaultValue : field.Text; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override async Task UpdateAsync(TextField field, UpdateFieldEditorContext context) + public override IDisplayResult Edit(TextField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Text); var settings = context.PartFieldDefinition.GetSettings(); + model.Text = context.IsNew && field.Text == null ? settings.DefaultValue : field.Text; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - if (settings.Required && string.IsNullOrWhiteSpace(field.Text)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + public override async Task UpdateAsync(TextField field, UpdateFieldEditorContext context) + { + await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Text); + var settings = context.PartFieldDefinition.GetSettings(); - return Edit(field, context); + if (settings.Required && string.IsNullOrWhiteSpace(field.Text)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Text), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TimeFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TimeFieldDisplayDriver.cs index 24392897836..cfa2e205838 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TimeFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/TimeFieldDisplayDriver.cs @@ -9,51 +9,50 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class TimeFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class TimeFieldDisplayDriver : ContentFieldDisplayDriver + internal readonly IStringLocalizer S; + + public TimeFieldDisplayDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public TimeFieldDisplayDriver(IStringLocalizer localizer) + public override IDisplayResult Display(TimeField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - S = localizer; - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Display(TimeField field, BuildFieldDisplayContext context) + public override IDisplayResult Edit(TimeField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Value = field.Value; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override IDisplayResult Edit(TimeField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => - { - model.Value = field.Value; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + public override async Task UpdateAsync(TimeField field, UpdateFieldEditorContext context) + { + await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); + var settings = context.PartFieldDefinition.GetSettings(); - public override async Task UpdateAsync(TimeField field, UpdateFieldEditorContext context) + if (settings.Required && field.Value == null) { - await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Value); - var settings = context.PartFieldDefinition.GetSettings(); - - if (settings.Required && field.Value == null) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } - - return Edit(field, context); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Value), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldDisplayDriver.cs index 5a52d950c44..b646aab6490 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldDisplayDriver.cs @@ -16,87 +16,86 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class UserPickerFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class UserPickerFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly ISession _session; + private readonly ISession _session; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public UserPickerFieldDisplayDriver( - ISession session, - IStringLocalizer stringLocalizer) - { - _session = session; - S = stringLocalizer; - } + public UserPickerFieldDisplayDriver( + ISession session, + IStringLocalizer stringLocalizer) + { + _session = session; + S = stringLocalizer; + } - public override IDisplayResult Display(UserPickerField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(UserPickerField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(UserPickerField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(UserPickerField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - model.UserIds = string.Join(",", field.UserIds); + model.UserIds = string.Join(",", field.UserIds); - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - model.TypePartDefinition = context.TypePartDefinition; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + model.TypePartDefinition = context.TypePartDefinition; - if (field.UserIds.Length > 0) - { - var users = (await _session.Query().Where(x => x.UserId.IsIn(field.UserIds)).ListAsync()) - .OrderBy(o => Array.FindIndex(field.UserIds, x => string.Equals(o.UserId, x, StringComparison.OrdinalIgnoreCase))); + if (field.UserIds.Length > 0) + { + var users = (await _session.Query().Where(x => x.UserId.IsIn(field.UserIds)).ListAsync()) + .OrderBy(o => Array.FindIndex(field.UserIds, x => string.Equals(o.UserId, x, StringComparison.OrdinalIgnoreCase))); - foreach (var user in users) + foreach (var user in users) + { + model.SelectedUsers.Add(new VueMultiselectUserViewModel { - model.SelectedUsers.Add(new VueMultiselectUserViewModel - { - Id = user.UserId, - DisplayText = user.UserName, - IsEnabled = user.IsEnabled - }); - } + Id = user.UserId, + DisplayText = user.UserName, + IsEnabled = user.IsEnabled + }); } - }); - } + } + }); + } - public override async Task UpdateAsync(UserPickerField field, UpdateFieldEditorContext context) - { - var viewModel = new EditUserPickerFieldViewModel(); + public override async Task UpdateAsync(UserPickerField field, UpdateFieldEditorContext context) + { + var viewModel = new EditUserPickerFieldViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.UserIds); - field.UserIds = viewModel.UserIds == null - ? [] : viewModel.UserIds.Split(',', StringSplitOptions.RemoveEmptyEntries); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.UserIds); + field.UserIds = viewModel.UserIds == null + ? [] : viewModel.UserIds.Split(',', StringSplitOptions.RemoveEmptyEntries); - var settings = context.PartFieldDefinition.GetSettings(); + var settings = context.PartFieldDefinition.GetSettings(); - if (settings.Required && field.UserIds.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.UserIds), S["The value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + if (settings.Required && field.UserIds.Length == 0) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.UserIds), S["The value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } - if (!settings.Multiple && field.UserIds.Length > 1) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.UserIds), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); - } + if (!settings.Multiple && field.UserIds.Length > 1) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(field.UserIds), S["The {0} field cannot contain multiple items.", context.PartFieldDefinition.DisplayName()]); + } - var users = await _session.Query().Where(x => x.UserId.IsIn(field.UserIds)).ListAsync(); - field.SetUserNames(users.Select(t => t.UserName).ToArray()); + var users = await _session.Query().Where(x => x.UserId.IsIn(field.UserIds)).ListAsync(); + field.SetUserNames(users.Select(t => t.UserName).ToArray()); - return Edit(field, context); - } + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldUserNamesDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldUserNamesDisplayDriver.cs index 91bdbaf8abc..1917b05a09e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldUserNamesDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/UserPickerFieldUserNamesDisplayDriver.cs @@ -4,20 +4,19 @@ using OrchardCore.ContentManagement.Display.Models; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class UserPickerFieldUserNamesDisplayDriver : ContentFieldDisplayDriver { - public sealed class UserPickerFieldUserNamesDisplayDriver : ContentFieldDisplayDriver + public override IDisplayResult Display(UserPickerField field, BuildFieldDisplayContext context) { - public override IDisplayResult Display(UserPickerField field, BuildFieldDisplayContext context) + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/YoutubeFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/YoutubeFieldDisplayDriver.cs index a001454f965..b57bcd6d89e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/YoutubeFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Drivers/YoutubeFieldDisplayDriver.cs @@ -12,88 +12,87 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Drivers +namespace OrchardCore.ContentFields.Drivers; + +public sealed class YoutubeFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class YoutubeFieldDisplayDriver : ContentFieldDisplayDriver + internal readonly IStringLocalizer S; + + public YoutubeFieldDisplayDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public YoutubeFieldDisplayDriver(IStringLocalizer localizer) + public override IDisplayResult Display(YoutubeField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - S = localizer; - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Display(YoutubeField field, BuildFieldDisplayContext context) - { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + public override IDisplayResult Edit(YoutubeField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => + { + model.RawAddress = field.RawAddress; + model.EmbeddedAddress = field.EmbeddedAddress; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override IDisplayResult Edit(YoutubeField field, BuildFieldEditorContext context) + public override async Task UpdateAsync(YoutubeField field, UpdateFieldEditorContext context) + { + var model = new EditYoutubeFieldViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix); + var settings = context.PartFieldDefinition.GetSettings(); + + if (settings.Required && string.IsNullOrWhiteSpace(model.RawAddress)) { - return Initialize(GetEditorShapeType(context), model => - { - model.RawAddress = field.RawAddress; - model.EmbeddedAddress = field.EmbeddedAddress; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.RawAddress), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); } - - public override async Task UpdateAsync(YoutubeField field, UpdateFieldEditorContext context) + else { - var model = new EditYoutubeFieldViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - var settings = context.PartFieldDefinition.GetSettings(); - - if (settings.Required && string.IsNullOrWhiteSpace(model.RawAddress)) + if (model.RawAddress != null) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.RawAddress), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } - else - { - if (model.RawAddress != null) - { - var uri = new Uri(model.RawAddress); + var uri = new Uri(model.RawAddress); - // If it is a url with QueryString. - if (!string.IsNullOrWhiteSpace(uri.Query)) + // If it is a url with QueryString. + if (!string.IsNullOrWhiteSpace(uri.Query)) + { + var query = QueryHelpers.ParseQuery(uri.Query); + if (query.TryGetValue("v", out var values)) { - var query = QueryHelpers.ParseQuery(uri.Query); - if (query.TryGetValue("v", out var values)) - { - model.EmbeddedAddress = $"{uri.GetLeftPart(UriPartial.Authority)}/embed/{values}"; - } - else - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.RawAddress), S["The format of the url is invalid"]); - } + model.EmbeddedAddress = $"{uri.GetLeftPart(UriPartial.Authority)}/embed/{values}"; } else { - var path = uri.AbsolutePath.Split('?')[0]; - model.EmbeddedAddress = $"{uri.GetLeftPart(UriPartial.Authority)}/embed/{path}"; + context.Updater.ModelState.AddModelError(Prefix, nameof(model.RawAddress), S["The format of the url is invalid"]); } - - field.RawAddress = model.RawAddress; - field.EmbeddedAddress = model.EmbeddedAddress; } else { - field.RawAddress = null; - field.EmbeddedAddress = null; + var path = uri.AbsolutePath.Split('?')[0]; + model.EmbeddedAddress = $"{uri.GetLeftPart(UriPartial.Authority)}/embed/{path}"; } - } - return Edit(field, context); + field.RawAddress = model.RawAddress; + field.EmbeddedAddress = model.EmbeddedAddress; + } + else + { + field.RawAddress = null; + field.EmbeddedAddress = null; + } } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/BooleanField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/BooleanField.cs index 4952fa7f69f..fd3c0364c54 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/BooleanField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/BooleanField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class BooleanField : ContentField { - public class BooleanField : ContentField - { - public bool Value { get; set; } - } + public bool Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/ContentPickerField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/ContentPickerField.cs index e0bd478e109..98ba3c61404 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/ContentPickerField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/ContentPickerField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class ContentPickerField : ContentField { - public class ContentPickerField : ContentField - { - public string[] ContentItemIds { get; set; } = []; - } + public string[] ContentItemIds { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateField.cs index 828075ab0ed..9fe8204559c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateField.cs @@ -1,10 +1,9 @@ using System; using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class DateField : ContentField { - public class DateField : ContentField - { - public DateTime? Value { get; set; } - } + public DateTime? Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateTimeField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateTimeField.cs index cb066cdbca4..bc8ae486966 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateTimeField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/DateTimeField.cs @@ -1,10 +1,9 @@ using System; using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class DateTimeField : ContentField { - public class DateTimeField : ContentField - { - public DateTime? Value { get; set; } - } + public DateTime? Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/HtmlField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/HtmlField.cs index fd25e7388a5..9a71287a4ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/HtmlField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/HtmlField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class HtmlField : ContentField { - public class HtmlField : ContentField - { - public string Html { get; set; } - } + public string Html { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LinkField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LinkField.cs index cc6f9781929..027ac33a02d 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LinkField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LinkField.cs @@ -1,13 +1,12 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class LinkField : ContentField { - public class LinkField : ContentField - { - public string Url { get; set; } + public string Url { get; set; } - public string Text { get; set; } + public string Text { get; set; } - public string Target { get; set; } - } + public string Target { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LocalizationSetContentPickerField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LocalizationSetContentPickerField.cs index 4b0085ffadd..4990bf84365 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LocalizationSetContentPickerField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/LocalizationSetContentPickerField.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +[RequireFeatures("OrchardCore.ContentLocalization")] +public class LocalizationSetContentPickerField : ContentField { - [RequireFeatures("OrchardCore.ContentLocalization")] - public class LocalizationSetContentPickerField : ContentField - { - public string[] LocalizationSets { get; set; } = []; - } + public string[] LocalizationSets { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/MultiTextField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/MultiTextField.cs index b48f8db2219..f49301261e6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/MultiTextField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/MultiTextField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class MultiTextField : ContentField { - public class MultiTextField : ContentField - { - public string[] Values { get; set; } = []; - } + public string[] Values { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/NumericField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/NumericField.cs index 0e9e889f1e6..f5a9b3c296e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/NumericField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/NumericField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class NumericField : ContentField { - public class NumericField : ContentField - { - public decimal? Value { get; set; } - } + public decimal? Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TextField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TextField.cs index f85e5091fdb..6c3d562a5cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TextField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TextField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class TextField : ContentField { - public class TextField : ContentField - { - public string Text { get; set; } - } + public string Text { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TimeField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TimeField.cs index f00265a0dff..9ca12318857 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TimeField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/TimeField.cs @@ -1,10 +1,9 @@ using System; using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class TimeField : ContentField { - public class TimeField : ContentField - { - public TimeSpan? Value { get; set; } - } + public TimeSpan? Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserNamesExtensions.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserNamesExtensions.cs index 946eb526e9b..76937c377ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserNamesExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserNamesExtensions.cs @@ -1,31 +1,30 @@ using System.Text.Json.Nodes; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public static class UserNamesExtensions { - public static class UserNamesExtensions + /// + /// User names are a less well known property of a . + /// + /// + /// This property is stored when the is saved, not when the value changes. + /// + public static string[] GetUserNames(this UserPickerField userPickerField) { - /// - /// User names are a less well known property of a . - /// - /// - /// This property is stored when the is saved, not when the value changes. - /// - public static string[] GetUserNames(this UserPickerField userPickerField) - { - var userNames = (JsonArray)userPickerField.Content["UserNames"]; + var userNames = (JsonArray)userPickerField.Content["UserNames"]; - return userNames is not null ? userNames.ToObject() : []; - } + return userNames is not null ? userNames.ToObject() : []; + } - /// - /// User names are a less well known property of a . - /// - /// - /// This property is stored when the is saved, not when the value changes. - /// - public static void SetUserNames(this UserPickerField userPickerField, string[] userNames) - { - userPickerField.Content["UserNames"] = JArray.FromObject(userNames); - } + /// + /// User names are a less well known property of a . + /// + /// + /// This property is stored when the is saved, not when the value changes. + /// + public static void SetUserNames(this UserPickerField userPickerField, string[] userNames) + { + userPickerField.Content["UserNames"] = JArray.FromObject(userNames); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserPickerField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserPickerField.cs index c1e4004c257..0c1c1f5070c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserPickerField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/UserPickerField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class UserPickerField : ContentField { - public class UserPickerField : ContentField - { - public string[] UserIds { get; set; } = []; - } + public string[] UserIds { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/YoutubeField.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/YoutubeField.cs index d6b25c25768..067d7c8f1e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/YoutubeField.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Fields/YoutubeField.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentFields.Fields +namespace OrchardCore.ContentFields.Fields; + +public class YoutubeField : ContentField { - public class YoutubeField : ContentField - { - public string EmbeddedAddress { get; set; } - public string RawAddress { get; set; } - } + public string EmbeddedAddress { get; set; } + public string RawAddress { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs index a086ade21cb..fc4cdae8090 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ContentFieldsProvider.cs @@ -10,156 +10,155 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.GraphQL.Fields +namespace OrchardCore.ContentFields.GraphQL.Fields; + +public class ContentFieldsProvider : IContentFieldProvider { - public class ContentFieldsProvider : IContentFieldProvider + private static readonly FrozenDictionary _contentFieldTypeMappings = new Dictionary() { - private static readonly FrozenDictionary _contentFieldTypeMappings = new Dictionary() { + nameof(BooleanField), + new FieldTypeDescriptor { - nameof(BooleanField), - new FieldTypeDescriptor - { - Description = "Boolean field", - FieldType = typeof(BooleanGraphType), - UnderlyingType = typeof(BooleanField), - FieldAccessor = field => ((BooleanField)field).Value, - IndexType = typeof(BooleanFieldIndex), - Index = nameof(BooleanFieldIndex.Boolean) - } - }, - { - nameof(DateField), - new FieldTypeDescriptor - { - Description = "Date field", - FieldType = typeof(DateGraphType), - UnderlyingType = typeof(DateField), - FieldAccessor = field => ((DateField)field).Value, - IndexType = typeof(DateFieldIndex), - Index = nameof(DateFieldIndex.Date) - } - }, + Description = "Boolean field", + FieldType = typeof(BooleanGraphType), + UnderlyingType = typeof(BooleanField), + FieldAccessor = field => ((BooleanField)field).Value, + IndexType = typeof(BooleanFieldIndex), + Index = nameof(BooleanFieldIndex.Boolean) + } + }, + { + nameof(DateField), + new FieldTypeDescriptor { - nameof(DateTimeField), - new FieldTypeDescriptor - { - Description = "Date & time field", - FieldType = typeof(DateTimeGraphType), - UnderlyingType = typeof(DateTimeField), - FieldAccessor = field => ((DateTimeField)field).Value, - IndexType = typeof(DateTimeFieldIndex), - Index = nameof(DateTimeFieldIndex.DateTime) - } - }, + Description = "Date field", + FieldType = typeof(DateGraphType), + UnderlyingType = typeof(DateField), + FieldAccessor = field => ((DateField)field).Value, + IndexType = typeof(DateFieldIndex), + Index = nameof(DateFieldIndex.Date) + } + }, + { + nameof(DateTimeField), + new FieldTypeDescriptor { - nameof(NumericField), - new FieldTypeDescriptor - { - Description = "Numeric field", - FieldType = typeof(DecimalGraphType), - UnderlyingType = typeof(NumericField), - FieldAccessor = field => ((NumericField)field).Value, - IndexType = typeof(NumericFieldIndex), - Index = nameof(NumericFieldIndex.Numeric) - } - }, + Description = "Date & time field", + FieldType = typeof(DateTimeGraphType), + UnderlyingType = typeof(DateTimeField), + FieldAccessor = field => ((DateTimeField)field).Value, + IndexType = typeof(DateTimeFieldIndex), + Index = nameof(DateTimeFieldIndex.DateTime) + } + }, + { + nameof(NumericField), + new FieldTypeDescriptor { - nameof(TextField), - new FieldTypeDescriptor - { - Description = "Text field", - FieldType = typeof(StringGraphType), - UnderlyingType = typeof(TextField), - FieldAccessor = field => ((TextField)field).Text, - IndexType = typeof(TextFieldIndex), - Index = nameof(TextFieldIndex.Text) - } - }, + Description = "Numeric field", + FieldType = typeof(DecimalGraphType), + UnderlyingType = typeof(NumericField), + FieldAccessor = field => ((NumericField)field).Value, + IndexType = typeof(NumericFieldIndex), + Index = nameof(NumericFieldIndex.Numeric) + } + }, + { + nameof(TextField), + new FieldTypeDescriptor { - nameof(TimeField), - new FieldTypeDescriptor - { - Description = "Time field", - FieldType = typeof(TimeSpanGraphType), - UnderlyingType = typeof(TimeField), - FieldAccessor = field => ((TimeField)field).Value, - IndexType = typeof(TimeFieldIndex), - Index = nameof(TimeFieldIndex.Time) - } - }, + Description = "Text field", + FieldType = typeof(StringGraphType), + UnderlyingType = typeof(TextField), + FieldAccessor = field => ((TextField)field).Text, + IndexType = typeof(TextFieldIndex), + Index = nameof(TextFieldIndex.Text) + } + }, + { + nameof(TimeField), + new FieldTypeDescriptor { - nameof(MultiTextField), - new FieldTypeDescriptor - { - Description = "Multi text field", - FieldType = typeof(ListGraphType), - UnderlyingType = typeof(MultiTextField), - FieldAccessor = field => ((MultiTextField)field).Values, - } + Description = "Time field", + FieldType = typeof(TimeSpanGraphType), + UnderlyingType = typeof(TimeField), + FieldAccessor = field => ((TimeField)field).Value, + IndexType = typeof(TimeFieldIndex), + Index = nameof(TimeFieldIndex.Time) } - }.ToFrozenDictionary(); - - public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName) + }, { - if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldDescriptor)) + nameof(MultiTextField), + new FieldTypeDescriptor { - return null; + Description = "Multi text field", + FieldType = typeof(ListGraphType), + UnderlyingType = typeof(MultiTextField), + FieldAccessor = field => ((MultiTextField)field).Values, } + } + }.ToFrozenDictionary(); + + public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName) + { + if (!_contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldDescriptor)) + { + return null; + } - return new FieldType + return new FieldType + { + Name = customFieldName ?? field.Name, + Description = fieldDescriptor.Description, + Type = fieldDescriptor.FieldType, + Resolver = new FuncFieldResolver(context => { - Name = customFieldName ?? field.Name, - Description = fieldDescriptor.Description, - Type = fieldDescriptor.FieldType, - Resolver = new FuncFieldResolver(context => - { - // Check if part has been collapsed by trying to get the parent part. - ContentElement contentPart = context.Source.Get(field.PartDefinition.Name); + // Check if part has been collapsed by trying to get the parent part. + ContentElement contentPart = context.Source.Get(field.PartDefinition.Name); - // Part is not collapsed, access field directly. - contentPart ??= context.Source; + // Part is not collapsed, access field directly. + contentPart ??= context.Source; - var contentField = contentPart?.Get(fieldDescriptor.UnderlyingType, field.Name); + var contentField = contentPart?.Get(fieldDescriptor.UnderlyingType, field.Name); - contentField ??= context.Source.Get(fieldDescriptor.UnderlyingType, field.Name); + contentField ??= context.Source.Get(fieldDescriptor.UnderlyingType, field.Name); - return contentField == null ? null : fieldDescriptor.FieldAccessor(contentField); - }), - }; - } + return contentField == null ? null : fieldDescriptor.FieldAccessor(contentField); + }), + }; + } - public bool HasField(ISchema schema, ContentPartFieldDefinition field) => _contentFieldTypeMappings.ContainsKey(field.FieldDefinition.Name); + public bool HasField(ISchema schema, ContentPartFieldDefinition field) => _contentFieldTypeMappings.ContainsKey(field.FieldDefinition.Name); - public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field) + public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field) + { + if (!HasFieldIndex(field)) { - if (!HasFieldIndex(field)) - { - return null; - } - - var fieldDescriptor = _contentFieldTypeMappings[field.FieldDefinition.Name]; - - return new FieldTypeIndexDescriptor - { - Index = fieldDescriptor.Index, - IndexType = fieldDescriptor.IndexType - }; + return null; } - public bool HasFieldIndex(ContentPartFieldDefinition field) => - _contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldTypeDescriptor) && - fieldTypeDescriptor.IndexType != null && - !string.IsNullOrWhiteSpace(fieldTypeDescriptor.Index); + var fieldDescriptor = _contentFieldTypeMappings[field.FieldDefinition.Name]; - private sealed class FieldTypeDescriptor + return new FieldTypeIndexDescriptor { - public string Description { get; set; } - public Type FieldType { get; set; } - public Type UnderlyingType { get; set; } - public Func FieldAccessor { get; set; } - public string Index { get; set; } - public Type IndexType { get; set; } - } + Index = fieldDescriptor.Index, + IndexType = fieldDescriptor.IndexType + }; + } + + public bool HasFieldIndex(ContentPartFieldDefinition field) => + _contentFieldTypeMappings.TryGetValue(field.FieldDefinition.Name, out var fieldTypeDescriptor) && + fieldTypeDescriptor.IndexType != null && + !string.IsNullOrWhiteSpace(fieldTypeDescriptor.Index); + + private sealed class FieldTypeDescriptor + { + public string Description { get; set; } + public Type FieldType { get; set; } + public Type UnderlyingType { get; set; } + public Func FieldAccessor { get; set; } + public string Index { get; set; } + public Type IndexType { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ObjectGraphTypeFieldProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ObjectGraphTypeFieldProvider.cs index 79bac8e52df..9228cebf9c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ObjectGraphTypeFieldProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Fields/ObjectGraphTypeFieldProvider.cs @@ -6,56 +6,55 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.GraphQL.Fields +namespace OrchardCore.ContentFields.GraphQL.Fields; + +public class ObjectGraphTypeFieldProvider : IContentFieldProvider { - public class ObjectGraphTypeFieldProvider : IContentFieldProvider + public ObjectGraphTypeFieldProvider() { - public ObjectGraphTypeFieldProvider() - { - } + } - public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName) - { - var queryGraphType = GetObjectGraphType(schema, field); + public FieldType GetField(ISchema schema, ContentPartFieldDefinition field, string namedPartTechnicalName, string customFieldName) + { + var queryGraphType = GetObjectGraphType(schema, field); - if (queryGraphType != null) + if (queryGraphType != null) + { + return new FieldType { - return new FieldType + Name = customFieldName ?? field.Name, + Description = field.FieldDefinition.Name, + ResolvedType = queryGraphType, + Resolver = new FuncFieldResolver(context => { - Name = customFieldName ?? field.Name, - Description = field.FieldDefinition.Name, - ResolvedType = queryGraphType, - Resolver = new FuncFieldResolver(context => - { - var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType - .GetGenericArguments().First(); - - // Check if part has been collapsed by trying to get the parent part. - ContentElement contentPart = context.Source.Get(field.PartDefinition.Name); - - // Part is not collapsed, access field directly. - contentPart ??= context.Source; - - var contentField = contentPart?.Get(typeToResolve, field.Name); - return contentField; - }) - }; - } - - return null; - } + var typeToResolve = context.FieldDefinition.ResolvedType.GetType().BaseType + .GetGenericArguments().First(); - public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field) - { - return null; - } + // Check if part has been collapsed by trying to get the parent part. + ContentElement contentPart = context.Source.Get(field.PartDefinition.Name); + + // Part is not collapsed, access field directly. + contentPart ??= context.Source; - public bool HasField(ISchema schema, ContentPartFieldDefinition field) => GetObjectGraphType(schema, field) != null; + var contentField = contentPart?.Get(typeToResolve, field.Name); + return contentField; + }) + }; + } - public bool HasFieldIndex(ContentPartFieldDefinition field) => false; + return null; + } - private static IObjectGraphType GetObjectGraphType(ISchema schema, ContentPartFieldDefinition field) => - schema.AdditionalTypeInstances - .FirstOrDefault(x => x is IObjectGraphType && x.GetType().BaseType.GetGenericArguments().First().Name == field.FieldDefinition.Name) as IObjectGraphType; + public FieldTypeIndexDescriptor GetFieldIndex(ContentPartFieldDefinition field) + { + return null; } + + public bool HasField(ISchema schema, ContentPartFieldDefinition field) => GetObjectGraphType(schema, field) != null; + + public bool HasFieldIndex(ContentPartFieldDefinition field) => false; + + private static IObjectGraphType GetObjectGraphType(ISchema schema, ContentPartFieldDefinition field) => + schema.AdditionalTypeInstances + .FirstOrDefault(x => x is IObjectGraphType && x.GetType().BaseType.GetGenericArguments().First().Name == field.FieldDefinition.Name) as IObjectGraphType; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs index 7def97b2aa1..4cbb31e4766 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Startup.cs @@ -7,29 +7,28 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.GraphQL +namespace OrchardCore.ContentFields.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - } + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); } +} - [RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.ContentFields.Indexing.SQL")] - public class IndexStartup : StartupBase +[RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.ContentFields.Indexing.SQL")] +public class IndexStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddContentFieldsInputGraphQL(); - } + services.AddContentFieldsInputGraphQL(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs index dbbf3464b65..466945adba4 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/ContentPickerFieldQueryObjectType.cs @@ -8,34 +8,33 @@ using OrchardCore.ContentManagement.GraphQL; using OrchardCore.ContentManagement.GraphQL.Queries.Types; -namespace OrchardCore.ContentFields.GraphQL +namespace OrchardCore.ContentFields.GraphQL; + +public class ContentPickerFieldQueryObjectType : ObjectGraphType { - public class ContentPickerFieldQueryObjectType : ObjectGraphType + public ContentPickerFieldQueryObjectType() { - public ContentPickerFieldQueryObjectType() - { - Name = nameof(ContentPickerField); + Name = nameof(ContentPickerField); - Field, IEnumerable>("contentItemIds") - .Description("content item ids") - .PagingArguments() - .Resolve(x => - { - return x.Page(x.Source.ContentItemIds); - }); + Field, IEnumerable>("contentItemIds") + .Description("content item ids") + .PagingArguments() + .Resolve(x => + { + return x.Page(x.Source.ContentItemIds); + }); - Field, IEnumerable>("contentItems") - .Description("the content items") - .PagingArguments() - .ResolveAsync(x => - { - var contentItemLoader = x.GetOrAddPublishedContentItemByIdDataLoader(); + Field, IEnumerable>("contentItems") + .Description("the content items") + .PagingArguments() + .ResolveAsync(x => + { + var contentItemLoader = x.GetOrAddPublishedContentItemByIdDataLoader(); - return (contentItemLoader.LoadAsync(x.Page(x.Source.ContentItemIds))).Then(itemResultSet => - { - return itemResultSet.SelectMany(x => x); - }); + return (contentItemLoader.LoadAsync(x.Page(x.Source.ContentItemIds))).Then(itemResultSet => + { + return itemResultSet.SelectMany(x => x); }); - } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/HtmlFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/HtmlFieldQueryObjectType.cs index b9c6bc98910..6d994a440fd 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/HtmlFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/HtmlFieldQueryObjectType.cs @@ -19,61 +19,60 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.ContentFields.GraphQL +namespace OrchardCore.ContentFields.GraphQL; + +public class HtmlFieldQueryObjectType : ObjectGraphType { - public class HtmlFieldQueryObjectType : ObjectGraphType + public HtmlFieldQueryObjectType(IStringLocalizer S) { - public HtmlFieldQueryObjectType(IStringLocalizer S) - { - Name = nameof(HtmlField); - Description = S["Content stored as HTML."]; + Name = nameof(HtmlField); + Description = S["Content stored as HTML."]; - Field("html") - .Description(S["the HTML content"]) - .ResolveLockedAsync(RenderHtml); - } + Field("html") + .Description(S["the HTML content"]) + .ResolveLockedAsync(RenderHtml); + } - private static async ValueTask RenderHtml(IResolveFieldContext ctx) - { - var serviceProvider = ctx.RequestServices; - var shortcodeService = serviceProvider.GetRequiredService(); - var contentDefinitionManager = serviceProvider.GetRequiredService(); + private static async ValueTask RenderHtml(IResolveFieldContext ctx) + { + var serviceProvider = ctx.RequestServices; + var shortcodeService = serviceProvider.GetRequiredService(); + var contentDefinitionManager = serviceProvider.GetRequiredService(); - var jObject = (JsonObject)ctx.Source.Content; - // The JObject.Path is consistent here even when contained in a bag part. - var jsonPath = jObject.GetNormalizedPath(); - var paths = jsonPath.Split('.'); - var partName = paths[0]; - var fieldName = paths[1]; - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); - var contentPartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, partName, StringComparison.Ordinal)); - var contentPartFieldDefinition = contentPartDefinition.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, fieldName, StringComparison.Ordinal)); - var settings = contentPartFieldDefinition.GetSettings(); + var jObject = (JsonObject)ctx.Source.Content; + // The JObject.Path is consistent here even when contained in a bag part. + var jsonPath = jObject.GetNormalizedPath(); + var paths = jsonPath.Split('.'); + var partName = paths[0]; + var fieldName = paths[1]; + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); + var contentPartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, partName, StringComparison.Ordinal)); + var contentPartFieldDefinition = contentPartDefinition.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, fieldName, StringComparison.Ordinal)); + var settings = contentPartFieldDefinition.GetSettings(); - var html = ctx.Source.Html; + var html = ctx.Source.Html; - if (!settings.SanitizeHtml) + if (!settings.SanitizeHtml) + { + var model = new EditHtmlFieldViewModel() { - var model = new EditHtmlFieldViewModel() - { - Html = ctx.Source.Html, - Field = ctx.Source, - Part = ctx.Source.ContentItem.Get(partName), - PartFieldDefinition = contentPartFieldDefinition - }; - var liquidTemplateManager = serviceProvider.GetRequiredService(); - var htmlEncoder = serviceProvider.GetService(); - - html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(ctx.Source.ContentItem) }); - } + Html = ctx.Source.Html, + Field = ctx.Source, + Part = ctx.Source.ContentItem.Get(partName), + PartFieldDefinition = contentPartFieldDefinition + }; + var liquidTemplateManager = serviceProvider.GetRequiredService(); + var htmlEncoder = serviceProvider.GetService(); - return await shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = ctx.Source.ContentItem, - ["PartFieldDefinition"] = contentPartFieldDefinition - }); + html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(ctx.Source.ContentItem) }); } + + return await shortcodeService.ProcessAsync(html, + new Context + { + ["ContentItem"] = ctx.Source.ContentItem, + ["PartFieldDefinition"] = contentPartFieldDefinition + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs index bcd85babb09..50c239be081 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/LinkFieldQueryObjectType.cs @@ -1,17 +1,16 @@ using GraphQL.Types; using OrchardCore.ContentFields.Fields; -namespace OrchardCore.ContentFields.GraphQL.Types +namespace OrchardCore.ContentFields.GraphQL.Types; + +public class LinkFieldQueryObjectType : ObjectGraphType { - public class LinkFieldQueryObjectType : ObjectGraphType + public LinkFieldQueryObjectType() { - public LinkFieldQueryObjectType() - { - Name = nameof(LinkField); + Name = nameof(LinkField); - Field(x => x.Url, nullable: true).Description("the url of the link"); - Field(x => x.Text, nullable: true).Description("the text of the link"); - Field(x => x.Target, nullable: true).Description("the target of the link"); - } + Field(x => x.Url, nullable: true).Description("the url of the link"); + Field(x => x.Text, nullable: true).Description("the text of the link"); + Field(x => x.Target, nullable: true).Description("the target of the link"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs index 8f64ae91467..6568aba1b80 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/GraphQL/Types/UserPickerFieldQueryObjectType.cs @@ -14,51 +14,50 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentFields.GraphQL +namespace OrchardCore.ContentFields.GraphQL; + +public class UserPickerFieldQueryObjectType : ObjectGraphType { - public class UserPickerFieldQueryObjectType : ObjectGraphType + public UserPickerFieldQueryObjectType(IStringLocalizer S) { - public UserPickerFieldQueryObjectType(IStringLocalizer S) - { - Name = nameof(UserPickerField); + Name = nameof(UserPickerField); - Field, IEnumerable>("userIds") - .Description(S["user ids"]) - .PagingArguments() - .Resolve(resolve => - { - return resolve.Page(resolve.Source.UserIds); - }); + Field, IEnumerable>("userIds") + .Description(S["user ids"]) + .PagingArguments() + .Resolve(resolve => + { + return resolve.Page(resolve.Source.UserIds); + }); - Field, IEnumerable>("users") - .Description(S["the user items"]) - .PagingArguments() - .ResolveAsync(resolve => + Field, IEnumerable>("users") + .Description(S["the user items"]) + .PagingArguments() + .ResolveAsync(resolve => + { + var userLoader = GetOrAddUserProfileByIdDataLoader(resolve); + return userLoader.LoadAsync(resolve.Page(resolve.Source.UserIds)).Then(itemResultSet => { - var userLoader = GetOrAddUserProfileByIdDataLoader(resolve); - return userLoader.LoadAsync(resolve.Page(resolve.Source.UserIds)).Then(itemResultSet => - { - return itemResultSet.SelectMany(users => users); - }); + return itemResultSet.SelectMany(users => users); }); - } + }); + } - private static IDataLoader> GetOrAddUserProfileByIdDataLoader(IResolveFieldContext context) - { - var dataLoaderContextAccessor = context.RequestServices.GetRequiredService(); + private static IDataLoader> GetOrAddUserProfileByIdDataLoader(IResolveFieldContext context) + { + var dataLoaderContextAccessor = context.RequestServices.GetRequiredService(); - return dataLoaderContextAccessor.Context.GetOrAddCollectionBatchLoader("GetOrAddUserByIds", async (IEnumerable userIds) => + return dataLoaderContextAccessor.Context.GetOrAddCollectionBatchLoader("GetOrAddUserByIds", async (IEnumerable userIds) => + { + if (userIds == null || !userIds.Any()) { - if (userIds == null || !userIds.Any()) - { - return default; - } + return default; + } - var session = context.RequestServices.GetService(); - var users = await session.Query(user => user.UserId.IsIn(userIds)).ListAsync(); + var session = context.RequestServices.GetService(); + var users = await session.Query(user => user.UserId.IsIn(userIds)).ListAsync(); - return users.ToLookup(user => user.UserId); - }); - } + return users.ToLookup(user => user.UserId); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/BooleanFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/BooleanFieldIndexHandler.cs index b44eeb0b0f8..1b5a3392012 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/BooleanFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/BooleanFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class BooleanFieldIndexHandler : ContentFieldIndexHandler { - public class BooleanFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(BooleanField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(BooleanField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Value, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Value, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/ContentPickerFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/ContentPickerFieldIndexHandler.cs index 6b594d4a035..592e06f6409 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/ContentPickerFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/ContentPickerFieldIndexHandler.cs @@ -3,33 +3,32 @@ using OrchardCore.Contents.Indexing; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class ContentPickerFieldIndexHandler : ContentFieldIndexHandler { - public class ContentPickerFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(ContentPickerField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(ContentPickerField field, BuildFieldIndexContext context) - { - var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; + var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - if (field.ContentItemIds.Length > 0) + if (field.ContentItemIds.Length > 0) + { + foreach (var contentItemId in field.ContentItemIds) { - foreach (var contentItemId in field.ContentItemIds) + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, contentItemId, options); - } + context.DocumentIndex.Set(key, contentItemId, options); } } - else + } + else + { + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); - } + context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); } - - return Task.CompletedTask; } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateFieldIndexHandler.cs index 54569e657c3..1609197c74a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class DateFieldIndexHandler : ContentFieldIndexHandler { - public class DateFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(DateField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(DateField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Value, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Value, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateTimeFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateTimeFieldIndexHandler.cs index 07ee7dd971e..6ac15b84ac1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateTimeFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/DateTimeFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class DateTimeFieldIndexHandler : ContentFieldIndexHandler { - public class DateTimeFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(DateTimeField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(DateTimeField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Value, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Value, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/HtmlFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/HtmlFieldIndexHandler.cs index 35c72bba4fe..1254433b08f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/HtmlFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/HtmlFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class HtmlFieldIndexHandler : ContentFieldIndexHandler { - public class HtmlFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(HtmlField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(HtmlField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Html, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Html, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LinkFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LinkFieldIndexHandler.cs index 6be2dc7814d..06c720c548c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LinkFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LinkFieldIndexHandler.cs @@ -2,22 +2,21 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class LinkFieldIndexHandler : ContentFieldIndexHandler { - public class LinkFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(LinkField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(LinkField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Url, options); - context.DocumentIndex.Set(key, field.Text, options); - context.DocumentIndex.Set(key, field.Target, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Url, options); + context.DocumentIndex.Set(key, field.Text, options); + context.DocumentIndex.Set(key, field.Target, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LocalizationSetContentPickerFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LocalizationSetContentPickerFieldIndexHandler.cs index eea41bd5bf8..11a89245a7a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LocalizationSetContentPickerFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/LocalizationSetContentPickerFieldIndexHandler.cs @@ -4,34 +4,33 @@ using OrchardCore.Indexing; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +[RequireFeatures("OrchardCore.ContentLocalization")] +public class LocalizationSetContentPickerFieldIndexHandler : ContentFieldIndexHandler { - [RequireFeatures("OrchardCore.ContentLocalization")] - public class LocalizationSetContentPickerFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(LocalizationSetContentPickerField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(LocalizationSetContentPickerField field, BuildFieldIndexContext context) - { - var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; + var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - if (field.LocalizationSets.Length > 0) + if (field.LocalizationSets.Length > 0) + { + foreach (var localizationSet in field.LocalizationSets) { - foreach (var localizationSet in field.LocalizationSets) + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, localizationSet, options); - } + context.DocumentIndex.Set(key, localizationSet, options); } } - else + } + else + { + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); - } + context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); } - - return Task.CompletedTask; } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/MultiTextFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/MultiTextFieldIndexHandler.cs index 00b420e1946..73f5b820d4f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/MultiTextFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/MultiTextFieldIndexHandler.cs @@ -2,23 +2,22 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class MultiTextFieldIndexHandler : ContentFieldIndexHandler { - public class MultiTextFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(MultiTextField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(MultiTextField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); + var options = context.Settings.ToOptions(); - foreach (var key in context.Keys) + foreach (var key in context.Keys) + { + foreach (var value in field.Values) { - foreach (var value in field.Values) - { - context.DocumentIndex.Set(key, value, options); - } + context.DocumentIndex.Set(key, value, options); } - - return Task.CompletedTask; } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/NumericFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/NumericFieldIndexHandler.cs index 1f9e38ce499..f20f5656e1a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/NumericFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/NumericFieldIndexHandler.cs @@ -3,30 +3,29 @@ using OrchardCore.ContentFields.Settings; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class NumericFieldIndexHandler : ContentFieldIndexHandler { - public class NumericFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(NumericField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(NumericField field, BuildFieldIndexContext context) - { - var settings = context.ContentPartFieldDefinition.GetSettings(); - var options = context.Settings.ToOptions(); + var settings = context.ContentPartFieldDefinition.GetSettings(); + var options = context.Settings.ToOptions(); - var isInteger = settings.Scale == 0; + var isInteger = settings.Scale == 0; - foreach (var key in context.Keys) + foreach (var key in context.Keys) + { + if (isInteger) { - if (isInteger) - { - context.DocumentIndex.Set(key, (int?)field.Value, options); + context.DocumentIndex.Set(key, (int?)field.Value, options); - continue; - } - - context.DocumentIndex.Set(key, field.Value, options); + continue; } - return Task.CompletedTask; + context.DocumentIndex.Set(key, field.Value, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/BooleanFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/BooleanFieldIndexProvider.cs index 80f3fc9c184..fb0a472f18b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/BooleanFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/BooleanFieldIndexProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class BooleanFieldIndex : ContentFieldIndex { - public class BooleanFieldIndex : ContentFieldIndex + public bool? Boolean { get; set; } +} + +public class BooleanFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public BooleanFieldIndexProvider(IServiceProvider serviceProvider) { - public bool? Boolean { get; set; } + _serviceProvider = serviceProvider; } - public class BooleanFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public BooleanFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for BooleanField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for BooleanField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(BooleanField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(BooleanField))) + .ToArray(); - // This type doesn't have any BooleanField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any BooleanField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new BooleanFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Boolean = pair.Field.Value, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new BooleanFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Boolean = pair.Field.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentFieldIndexProvider.cs index 8c7f0a3bf60..c9efc7a654f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentFieldIndexProvider.cs @@ -2,20 +2,19 @@ using OrchardCore.Data; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public abstract class ContentFieldIndex : MapIndex { - public abstract class ContentFieldIndex : MapIndex - { - public string ContentItemId { get; set; } - public string ContentItemVersionId { get; set; } - public string ContentType { get; set; } - public string ContentPart { get; set; } - public string ContentField { get; set; } - public bool Published { get; set; } - public bool Latest { get; set; } - } + public string ContentItemId { get; set; } + public string ContentItemVersionId { get; set; } + public string ContentType { get; set; } + public string ContentPart { get; set; } + public string ContentField { get; set; } + public bool Published { get; set; } + public bool Latest { get; set; } +} - public abstract class ContentFieldIndexProvider : IndexProvider, IScopedIndexProvider - { - } +public abstract class ContentFieldIndexProvider : IndexProvider, IScopedIndexProvider +{ } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentPickerFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentPickerFieldIndexProvider.cs index e12ede1a931..ca9798bb712 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentPickerFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/ContentPickerFieldIndexProvider.cs @@ -8,83 +8,82 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class ContentPickerFieldIndex : ContentFieldIndex { - public class ContentPickerFieldIndex : ContentFieldIndex + public string SelectedContentItemId { get; set; } +} + +public class ContentPickerFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public ContentPickerFieldIndexProvider(IServiceProvider serviceProvider) { - public string SelectedContentItemId { get; set; } + _serviceProvider = serviceProvider; } - public class ContentPickerFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public ContentPickerFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for ContentPickerField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for ContentPickerField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(ContentPickerField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(ContentPickerField))) + .ToArray(); - // This type doesn't have any ContentPickerField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any ContentPickerField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - // Get all field values - return fieldDefinitions - .GetContentFields(contentItem) - .SelectMany(pair => - pair.Field.ContentItemIds.Select(id => (pair.Definition, ContentItemId: id))) - .Select(pair => - new ContentPickerFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - SelectedContentItemId = pair.ContentItemId, - }); - }); - } + // Get all field values + return fieldDefinitions + .GetContentFields(contentItem) + .SelectMany(pair => + pair.Field.ContentItemIds.Select(id => (pair.Definition, ContentItemId: id))) + .Select(pair => + new ContentPickerFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + SelectedContentItemId = pair.ContentItemId, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateFieldIndexProvider.cs index bd6aac2a50f..1cdb6c24e04 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateFieldIndexProvider.cs @@ -8,79 +8,78 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class DateFieldIndex : ContentFieldIndex +{ + public DateTime? Date { get; set; } +} + +public class DateFieldIndexProvider : ContentFieldIndexProvider { - public class DateFieldIndex : ContentFieldIndex + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public DateFieldIndexProvider(IServiceProvider serviceProvider) { - public DateTime? Date { get; set; } + _serviceProvider = serviceProvider; } - public class DateFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public DateFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) + { + return null; + } - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Search for DateField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // Search for DateField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(DateField))) + .ToArray(); - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(DateField))) - .ToArray(); + // This type doesn't have any DateField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - // This type doesn't have any DateField, ignore it - if (fieldDefinitions.Length == 0) + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => new DateFieldIndex { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } - - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => new DateFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Date = pair.Field.Value, - }); - }); - } + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Date = pair.Field.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateTimeFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateTimeFieldIndexProvider.cs index 823430558d5..14ae0e5f9fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateTimeFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/DateTimeFieldIndexProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class DateTimeFieldIndex : ContentFieldIndex { - public class DateTimeFieldIndex : ContentFieldIndex + public DateTime? DateTime { get; set; } +} + +public class DateTimeFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public DateTimeFieldIndexProvider(IServiceProvider serviceProvider) { - public DateTime? DateTime { get; set; } + _serviceProvider = serviceProvider; } - public class DateTimeFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public DateTimeFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for DateTimeField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for DateTimeField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(DateTimeField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(DateTimeField))) + .ToArray(); - // This type doesn't have any DateTimeField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any DateTimeField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new DateTimeFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - DateTime = pair.Field.Value, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new DateTimeFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + DateTime = pair.Field.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/HtmlFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/HtmlFieldIndexProvider.cs index 090a62ff547..e2916551bdb 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/HtmlFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/HtmlFieldIndexProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class HtmlFieldIndex : ContentFieldIndex { - public class HtmlFieldIndex : ContentFieldIndex + public string Html { get; set; } +} + +public class HtmlFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public HtmlFieldIndexProvider(IServiceProvider serviceProvider) { - public string Html { get; set; } + _serviceProvider = serviceProvider; } - public class HtmlFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public HtmlFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for Html fields - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for Html fields + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(HtmlField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(HtmlField))) + .ToArray(); - // This type doesn't have any HtmlField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any HtmlField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new HtmlFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Html = pair.Field.Html, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new HtmlFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Html = pair.Field.Html, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/LinkFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/LinkFieldIndexProvider.cs index eb1e7af2d59..f6d4f3a0d60 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/LinkFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/LinkFieldIndexProvider.cs @@ -8,94 +8,93 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class LinkFieldIndex : ContentFieldIndex { - public class LinkFieldIndex : ContentFieldIndex - { - // Maximum length that MySql can support in an index under utf8 collation is 768, - // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). - // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' boolean. - public const int MaxUrlSize = 766; - public const int MaxTextSize = 766; + // Maximum length that MySql can support in an index under utf8 collation is 768, + // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). + // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' boolean. + public const int MaxUrlSize = 766; + public const int MaxTextSize = 766; - public string Url { get; set; } - public string BigUrl { get; set; } - public string Text { get; set; } - public string BigText { get; set; } - } + public string Url { get; set; } + public string BigUrl { get; set; } + public string Text { get; set; } + public string BigText { get; set; } +} - public class LinkFieldIndexProvider : ContentFieldIndexProvider - { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; +public class LinkFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; - public LinkFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public LinkFieldIndexProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + public override void Describe(DescribeContext context) + { + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for LinkField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for LinkField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); - return null; - } + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(LinkField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(LinkField))) + .ToArray(); - // This type doesn't have any LinkField, ignore it. - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); + // This type doesn't have any LinkField, ignore it. + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); - return null; - } + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new LinkFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Url = pair.Field.Url?[..Math.Min(pair.Field.Url.Length, LinkFieldIndex.MaxUrlSize)], - BigUrl = pair.Field.Url, - Text = pair.Field.Text?[..Math.Min(pair.Field.Text.Length, LinkFieldIndex.MaxTextSize)], - BigText = pair.Field.Text, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new LinkFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Url = pair.Field.Url?[..Math.Min(pair.Field.Url.Length, LinkFieldIndex.MaxUrlSize)], + BigUrl = pair.Field.Url, + Text = pair.Field.Text?[..Math.Min(pair.Field.Text.Length, LinkFieldIndex.MaxTextSize)], + BigText = pair.Field.Text, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/Migrations.cs index b6edd3433e5..a6ca4ec046c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/Migrations.cs @@ -5,807 +5,806 @@ using OrchardCore.Data.Migration; using YesSql.Sql; -namespace OrchardCore.ContentFields.Indexing.SQL -{ - public sealed class Migrations : DataMigration - { - private readonly ILogger _logger; - - public Migrations(ILogger logger) - { - _logger = logger; - } - - public async Task CreateAsync() - { - // NOTE: The Text Length has been decreased from 4000 characters to 768. - // For existing SQL databases update the TextFieldIndex tables Text column length manually. - // INFO: The Text Length is now of 766 chars, but this is only used on a new installation. - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Text", column => column.Nullable().WithLength(TextFieldIndex.MaxTextSize)) - .Column("BigText", column => column.Nullable().Unlimited()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId_Text", - "DocumentId", - "Text(764)", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Boolean", column => column.Nullable()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_BooleanFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Boolean, Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_BooleanFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Boolean", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Numeric", column => column.Nullable()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId_Numeric", - "DocumentId", - "Numeric", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("DateTime", column => column.Nullable()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_DateTime", - "DocumentId", - "DateTime", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Date", column => column.Nullable()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); +namespace OrchardCore.ContentFields.Indexing.SQL; - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId_Date", - "DocumentId", - "ContentType", - "Date", - "Published", - "Latest") - ); +public sealed class Migrations : DataMigration +{ + private readonly ILogger _logger; - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("SelectedContentItemId", column => column.WithLength(26)) - ); + public Migrations(ILogger logger) + { + _logger = logger; + } - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); + public async Task CreateAsync() + { + // NOTE: The Text Length has been decreased from 4000 characters to 768. + // For existing SQL databases update the TextFieldIndex tables Text column length manually. + // INFO: The Text Length is now of 766 chars, but this is only used on a new installation. + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Text", column => column.Nullable().WithLength(TextFieldIndex.MaxTextSize)) + .Column("BigText", column => column.Nullable().Unlimited()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId_Text", + "DocumentId", + "Text(764)", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Boolean", column => column.Nullable()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_BooleanFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Boolean, Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_BooleanFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Boolean", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Numeric", column => column.Nullable()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId_Numeric", + "DocumentId", + "Numeric", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("DateTime", column => column.Nullable()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_DateTime", + "DocumentId", + "DateTime", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Date", column => column.Nullable()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId_Date", + "DocumentId", + "ContentType", + "Date", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("SelectedContentItemId", column => column.WithLength(26)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerField_DocumentId_SelectedItemId", + "DocumentId", + "SelectedContentItemId", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Time", column => column.Nullable()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", + "DocumentId", + "Time", + "Published", + "Latest") + ); + + // NOTE: The Url and Text Length has been decreased from 4000 characters to 768. + // For existing SQL databases update the LinkFieldIndex tables Url and Text column length manually. + // The BigText and BigUrl columns are new additions so will not be populated until the content item is republished. + // INFO: The Url and Text Length is now of 766 chars, but this is only used on a new installation. + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Url", column => column.Nullable().WithLength(LinkFieldIndex.MaxUrlSize)) + .Column("BigUrl", column => column.Nullable().Unlimited()) + .Column("Text", column => column.Nullable().WithLength(LinkFieldIndex.MaxTextSize)) + .Column("BigText", column => column.Nullable().Unlimited()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Url (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_Url", + "DocumentId", + "Url(764)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_Text", + "DocumentId", + "Text(764)", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Html", column => column.Nullable().Unlimited()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_HtmlFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_HtmlFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Value", column => column.Nullable().WithLength(MultiTextFieldIndex.MaxValueSize)) + .Column("BigValue", column => column.Nullable().Unlimited()) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Value (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_Value", + "DocumentId", + "Value(764)", + "Published", + "Latest") + ); + + // Shortcut other migration steps on new content definition schemas. + return 5; + } - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("BigUrl", column => column.Nullable().Unlimited())); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerField_DocumentId_SelectedItemId", - "DocumentId", - "SelectedContentItemId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("BigText", column => column.Nullable().Unlimited())); - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Time", column => column.Nullable()) - ); + return 2; + } - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("Value", column => column.Nullable().WithLength(MultiTextFieldIndex.MaxValueSize)) + .Column("BigValue", column => column.Nullable().Unlimited()) + ); + + return 3; + } - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TextFieldIndex_DocumentId_Text", + "DocumentId", + "Text(764)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_BooleanFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Boolean, Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_BooleanFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Boolean", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_NumericFieldIndex_DocumentId_Numeric", + "DocumentId", + "Numeric", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_DateTime", + "DocumentId", + "DateTime", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_DateFieldIndex_DocumentId_Date", + "DocumentId", + "Date", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContentPickerField_DocumentId_SelectedItemId", + "DocumentId", + "SelectedContentItemId", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", + "DocumentId", + "Time", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Url (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_Url", + "DocumentId", + "Url(764)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LinkFieldIndex_DocumentId_Text", + "DocumentId", + "Text(764)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_HtmlFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_HtmlFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + Value (764) + Published and Latest (1) = 767 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_Value", + "DocumentId", + "Value(764)", + "Published", + "Latest") + ); + + return 4; + } + // This code can be removed in a later version. + public async Task UpdateFrom4Async() + { + // Attempts to drop an index that existed only in RC2. + try + { await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", - "DocumentId", - "Time", - "Published", - "Latest") - ); - - // NOTE: The Url and Text Length has been decreased from 4000 characters to 768. - // For existing SQL databases update the LinkFieldIndex tables Url and Text column length manually. - // The BigText and BigUrl columns are new additions so will not be populated until the content item is republished. - // INFO: The Url and Text Length is now of 766 chars, but this is only used on a new installation. - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Url", column => column.Nullable().WithLength(LinkFieldIndex.MaxUrlSize)) - .Column("BigUrl", column => column.Nullable().Unlimited()) - .Column("Text", column => column.Nullable().WithLength(LinkFieldIndex.MaxTextSize)) - .Column("BigText", column => column.Nullable().Unlimited()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Url (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_Url", - "DocumentId", - "Url(764)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_Text", - "DocumentId", - "Text(764)", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Html", column => column.Nullable().Unlimited()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_HtmlFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_HtmlFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Value", column => column.Nullable().WithLength(MultiTextFieldIndex.MaxValueSize)) - .Column("BigValue", column => column.Nullable().Unlimited()) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") + .DropIndex("IDX_TimeFieldIndex_Time") ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Value (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_Value", - "DocumentId", - "Value(764)", - "Published", - "Latest") - ); - - // Shortcut other migration steps on new content definition schemas. - return 5; } - - // This code can be removed in a later version. - public async Task UpdateFrom1Async() + catch { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("BigUrl", column => column.Nullable().Unlimited())); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("BigText", column => column.Nullable().Unlimited())); - - return 2; + _logger.LogWarning("Failed to drop an index that does not exist 'IDX_TimeFieldIndex_Time'"); } - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("Value", column => column.Nullable().WithLength(MultiTextFieldIndex.MaxValueSize)) - .Column("BigValue", column => column.Nullable().Unlimited()) - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .DropIndex("IDX_TimeFieldIndex_DocumentId_Time") + ); - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom3Async() + // SqLite does not support dropping columns. + try { - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TextFieldIndex_DocumentId_Text", - "DocumentId", - "Text(764)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_BooleanFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Boolean, Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_BooleanFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Boolean", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_NumericFieldIndex_DocumentId_Numeric", - "DocumentId", - "Numeric", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateTimeFieldIndex_DocumentId_DateTime", - "DocumentId", - "DateTime", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_DateFieldIndex_DocumentId_Date", - "DocumentId", - "Date", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContentPickerField_DocumentId_SelectedItemId", - "DocumentId", - "SelectedContentItemId", - "Published", - "Latest") - ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); + .DropColumn("Time")); await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", - "DocumentId", - "Time", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Url (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_Url", - "DocumentId", - "Url(764)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Text (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LinkFieldIndex_DocumentId_Text", - "DocumentId", - "Text(764)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_HtmlFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_HtmlFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + Value (764) + Published and Latest (1) = 767 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_MultiTextFieldIndex_DocumentId_Value", - "DocumentId", - "Value(764)", - "Published", - "Latest") - ); - - return 4; + .AddColumn("Time", column => column.Nullable())); } - - // This code can be removed in a later version. - public async Task UpdateFrom4Async() + catch { - // Attempts to drop an index that existed only in RC2. - try - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .DropIndex("IDX_TimeFieldIndex_Time") - ); - } - catch - { - _logger.LogWarning("Failed to drop an index that does not exist 'IDX_TimeFieldIndex_Time'"); - } - - await SchemaBuilder.AlterIndexTableAsync(table => table - .DropIndex("IDX_TimeFieldIndex_DocumentId_Time") - ); - - // SqLite does not support dropping columns. - try - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .DropColumn("Time")); + _logger.LogWarning("Failed to alter 'Time' column. This is not an error when using SqLite"); + } - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Time", column => column.Nullable())); - } - catch - { - _logger.LogWarning("Failed to alter 'Time' column. This is not an error when using SqLite"); - } + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", + "DocumentId", + "Time", + "Published", + "Latest") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TimeFieldIndex_DocumentId_Time", - "DocumentId", - "Time", - "Published", - "Latest") - ); - - return 5; - } + return 5; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/MultiTextFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/MultiTextFieldIndexProvider.cs index 6e1d3710748..68a1711e8fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/MultiTextFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/MultiTextFieldIndexProvider.cs @@ -8,89 +8,88 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class MultiTextFieldIndex : ContentFieldIndex { - public class MultiTextFieldIndex : ContentFieldIndex - { - // Maximum length that MySql can support in an index under utf8 collation is 768, - // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). - // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' booleans. - public const int MaxValueSize = 766; + // Maximum length that MySql can support in an index under utf8 collation is 768, + // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). + // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' booleans. + public const int MaxValueSize = 766; - public string Value { get; set; } - public string BigValue { get; set; } - } + public string Value { get; set; } + public string BigValue { get; set; } +} - public class MultiTextFieldIndexProvider : ContentFieldIndexProvider - { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; +public class MultiTextFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; - public MultiTextFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public MultiTextFieldIndexProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + public override void Describe(DescribeContext context) + { + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for TextField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for TextField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(MultiTextField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(MultiTextField))) + .ToArray(); - // This type doesn't have any MultiTextField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any MultiTextField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .SelectMany(pair => - pair.Field.Values.Select(value => (pair.Definition, Value: value))) - .Select(pair => - new MultiTextFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Value = pair.Value?[..Math.Min(pair.Value.Length, MultiTextFieldIndex.MaxValueSize)], - BigValue = pair.Value, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .SelectMany(pair => + pair.Field.Values.Select(value => (pair.Definition, Value: value))) + .Select(pair => + new MultiTextFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Value = pair.Value?[..Math.Min(pair.Value.Length, MultiTextFieldIndex.MaxValueSize)], + BigValue = pair.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/NumericFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/NumericFieldIndexProvider.cs index e3636a97bcc..3b86830d963 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/NumericFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/NumericFieldIndexProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class NumericFieldIndex : ContentFieldIndex { - public class NumericFieldIndex : ContentFieldIndex + public decimal? Numeric { get; set; } +} + +public class NumericFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public NumericFieldIndexProvider(IServiceProvider serviceProvider) { - public decimal? Numeric { get; set; } + _serviceProvider = serviceProvider; } - public class NumericFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public NumericFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for NumericField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for NumericField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(NumericField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(NumericField))) + .ToArray(); - // This type doesn't have any NumericField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any NumericField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new NumericFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Numeric = pair.Field.Value, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new NumericFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Numeric = pair.Field.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TextFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TextFieldIndexProvider.cs index 3ab714e4ffd..04753a5f260 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TextFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TextFieldIndexProvider.cs @@ -8,87 +8,86 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class TextFieldIndex : ContentFieldIndex { - public class TextFieldIndex : ContentFieldIndex - { - // Maximum length that MySql can support in an index under utf8 collation is 768, - // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). - // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' booleans. - public const int MaxTextSize = 766; + // Maximum length that MySql can support in an index under utf8 collation is 768, + // minus 1 for the `DocumentId` integer (character size = integer size = 4 bytes). + // minus 1 (freeing 4 bytes) for the additional 'Published' and 'Latest' booleans. + public const int MaxTextSize = 766; - public string Text { get; set; } - public string BigText { get; set; } - } + public string Text { get; set; } + public string BigText { get; set; } +} - public class TextFieldIndexProvider : ContentFieldIndexProvider - { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; +public class TextFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; - public TextFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public TextFieldIndexProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + public override void Describe(DescribeContext context) + { + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for TextField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for TextField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TextField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TextField))) + .ToArray(); - // This type doesn't have any TextField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any TextField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new TextFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Text = pair.Field.Text?[..Math.Min(pair.Field.Text.Length, TextFieldIndex.MaxTextSize)], - BigText = pair.Field.Text, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new TextFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Text = pair.Field.Text?[..Math.Min(pair.Field.Text.Length, TextFieldIndex.MaxTextSize)], + BigText = pair.Field.Text, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TimeFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TimeFieldIndexProvider.cs index 535a856d7ee..004f5d8c43c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TimeFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/TimeFieldIndexProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class TimeFieldIndex : ContentFieldIndex { - public class TimeFieldIndex : ContentFieldIndex + public TimeSpan? Time { get; set; } +} + +public class TimeFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public TimeFieldIndexProvider(IServiceProvider serviceProvider) { - public TimeSpan? Time { get; set; } + _serviceProvider = serviceProvider; } - public class TimeFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public TimeFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for TimeField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for TimeField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TimeField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TimeField))) + .ToArray(); - // This type doesn't have any TimeField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any TimeField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .Select(pair => - new TimeFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - Time = pair.Field.Value, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .Select(pair => + new TimeFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + Time = pair.Field.Value, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerFieldIndexProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerFieldIndexProvider.cs index fd1f95c4f24..df6dddacd85 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerFieldIndexProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerFieldIndexProvider.cs @@ -8,82 +8,81 @@ using OrchardCore.ContentManagement.Metadata.Models; using YesSql.Indexes; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public class UserPickerFieldIndex : ContentFieldIndex { - public class UserPickerFieldIndex : ContentFieldIndex + public string SelectedUserId { get; set; } +} + +public class UserPickerFieldIndexProvider : ContentFieldIndexProvider +{ + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public UserPickerFieldIndexProvider(IServiceProvider serviceProvider) { - public string SelectedUserId { get; set; } + _serviceProvider = serviceProvider; } - public class UserPickerFieldIndexProvider : ContentFieldIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public UserPickerFieldIndexProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for ContentPickerField - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for ContentPickerField + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(UserPickerField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(UserPickerField))) + .ToArray(); - // This type doesn't have any UserPickerField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any UserPickerField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - return fieldDefinitions - .GetContentFields(contentItem) - .SelectMany(pair => - pair.Field.UserIds.Select(value => (pair.Definition, UserId: value))) - .Select(pair => - new UserPickerFieldIndex - { - Latest = contentItem.Latest, - Published = contentItem.Published, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - ContentType = contentItem.ContentType, - ContentPart = pair.Definition.ContentTypePartDefinition.Name, - ContentField = pair.Definition.Name, - SelectedUserId = pair.UserId, - }); - }); - } + return fieldDefinitions + .GetContentFields(contentItem) + .SelectMany(pair => + pair.Field.UserIds.Select(value => (pair.Definition, UserId: value))) + .Select(pair => + new UserPickerFieldIndex + { + Latest = contentItem.Latest, + Published = contentItem.Published, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + ContentType = contentItem.ContentType, + ContentPart = pair.Definition.ContentTypePartDefinition.Name, + ContentField = pair.Definition.Name, + SelectedUserId = pair.UserId, + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerMigrations.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerMigrations.cs index 88b701d2650..c658119f22e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerMigrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/SQL/UserPickerMigrations.cs @@ -3,89 +3,88 @@ using OrchardCore.Data.Migration; using YesSql.Sql; -namespace OrchardCore.ContentFields.Indexing.SQL +namespace OrchardCore.ContentFields.Indexing.SQL; + +public sealed class UserPickerMigrations : DataMigration { - public sealed class UserPickerMigrations : DataMigration + public async Task CreateAsync() { - public async Task CreateAsync() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ContentItemVersionId", column => column.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("Published", column => column.Nullable()) - .Column("Latest", column => column.Nullable()) - .Column("SelectedUserId") - ); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ContentItemVersionId", column => column.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("Published", column => column.Nullable()) + .Column("Latest", column => column.Nullable()) + .Column("SelectedUserId") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_SelectedUserId", - "DocumentId", - "SelectedUserId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_SelectedUserId", + "DocumentId", + "SelectedUserId", + "Published", + "Latest") + ); - // Shortcut other migration steps on new content definition schemas. - return 2; - } + // Shortcut other migration steps on new content definition schemas. + return 2; + } - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId", - "DocumentId", - "ContentItemId", - "ContentItemVersionId", - "Published", - "Latest") - ); + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId", + "DocumentId", + "ContentItemId", + "ContentItemVersionId", + "Published", + "Latest") + ); - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_SelectedUserId", - "DocumentId", - "SelectedUserId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_UserPickerFieldIndex_DocumentId_SelectedUserId", + "DocumentId", + "SelectedUserId", + "Published", + "Latest") + ); - return 2; - } + return 2; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TextFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TextFieldIndexHandler.cs index 7d409856d88..712cde95fbc 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TextFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TextFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class TextFieldIndexHandler : ContentFieldIndexHandler { - public class TextFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(TextField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(TextField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Text, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Text, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TimeFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TimeFieldIndexHandler.cs index df61e201a24..374712da9b3 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TimeFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/TimeFieldIndexHandler.cs @@ -3,26 +3,25 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class TimeFieldIndexHandler : ContentFieldIndexHandler { - public class TimeFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(TimeField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(TimeField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); + var options = context.Settings.ToOptions(); - DateTime? indexedValue = null; - if (field.Value.HasValue) - { - indexedValue = new DateTime(field.Value.Value.Ticks); - } - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, indexedValue, options); - } + DateTime? indexedValue = null; + if (field.Value.HasValue) + { + indexedValue = new DateTime(field.Value.Value.Ticks); + } - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, indexedValue, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/UserPickerFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/UserPickerFieldIndexHandler.cs index 882bd7d2414..0cbc7dfebda 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/UserPickerFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/UserPickerFieldIndexHandler.cs @@ -3,42 +3,41 @@ using OrchardCore.Contents.Indexing; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class UserPickerFieldIndexHandler : ContentFieldIndexHandler { - public class UserPickerFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(UserPickerField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(UserPickerField field, BuildFieldIndexContext context) - { - var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; + var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - if (field.UserIds.Length > 0) + if (field.UserIds.Length > 0) + { + foreach (var userId in field.UserIds) { - foreach (var userId in field.UserIds) - { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, userId, options); - } - } - - var userNames = field.GetUserNames(); - foreach (var userName in userNames) + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, userName, options); - } + context.DocumentIndex.Set(key, userId, options); } } - else + + var userNames = field.GetUserNames(); + foreach (var userName in userNames) { foreach (var key in context.Keys) { - context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); + context.DocumentIndex.Set(key, userName, options); } } - - return Task.CompletedTask; } + else + { + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, IndexingConstants.NullValue, options); + } + } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/YoutubeFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/YoutubeFieldIndexHandler.cs index 430ce70d291..29d87f91744 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/YoutubeFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Indexing/YoutubeFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentFields.Fields; using OrchardCore.Indexing; -namespace OrchardCore.ContentFields.Indexing +namespace OrchardCore.ContentFields.Indexing; + +public class YoutubeFieldIndexHandler : ContentFieldIndexHandler { - public class YoutubeFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(YoutubeField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(YoutubeField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.EmbeddedAddress, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.EmbeddedAddress, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/MediaShapes.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/MediaShapes.cs index 64e362deb59..f7b674b6e26 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/MediaShapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/MediaShapes.cs @@ -2,30 +2,29 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Media +namespace OrchardCore.ContentFields.Media; + +[RequireFeatures("OrchardCore.Media")] +public class MediaShapes : ShapeTableProvider { - [RequireFeatures("OrchardCore.Media")] - public class MediaShapes : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("HtmlField_Edit") - .OnDisplaying(displaying => - { - var editor = displaying.Shape; + builder.Describe("HtmlField_Edit") + .OnDisplaying(displaying => + { + var editor = displaying.Shape; - if (editor.Metadata.Type == "HtmlField_Edit__Wysiwyg") - { - editor.Metadata.Wrappers.Add("Media_Wrapper__HtmlField"); - } + if (editor.Metadata.Type == "HtmlField_Edit__Wysiwyg") + { + editor.Metadata.Wrappers.Add("Media_Wrapper__HtmlField"); + } - if (editor.Metadata.Type == "HtmlField_Edit__Trumbowyg") - { - editor.Metadata.Wrappers.Add("Media_Wrapper__HtmlField"); - } - }); + if (editor.Metadata.Type == "HtmlField_Edit__Trumbowyg") + { + editor.Metadata.Wrappers.Add("Media_Wrapper__HtmlField"); + } + }); - return ValueTask.CompletedTask; - } + return ValueTask.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/Startup.cs index 1daa4f35bbc..d1844ad0fcc 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Media/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Media +namespace OrchardCore.ContentFields.Media; + +[RequireFeatures("OrchardCore.Media")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Media")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Migrations.cs index 63cdceb1a0c..bc9fa87c282 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Migrations.cs @@ -7,102 +7,101 @@ using OrchardCore.Environment.Shell; using OrchardCore.Environment.Shell.Descriptor.Models; -namespace OrchardCore.ContentFields +namespace OrchardCore.ContentFields; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ShellDescriptor _shellDescriptor; + + public Migrations( + IContentDefinitionManager contentDefinitionManager, + ShellDescriptor shellDescriptor) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ShellDescriptor _shellDescriptor; + _contentDefinitionManager = contentDefinitionManager; + _shellDescriptor = shellDescriptor; + } - public Migrations( - IContentDefinitionManager contentDefinitionManager, - ShellDescriptor shellDescriptor) + // New installations don't need to be upgraded, but because there is no initial migration record, + // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. + public async Task CreateAsync() + { + if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.ContentFields")) { - _contentDefinitionManager = contentDefinitionManager; - _shellDescriptor = shellDescriptor; + await UpgradeAsync(); } - // New installations don't need to be upgraded, but because there is no initial migration record, - // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. - public async Task CreateAsync() - { - if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.ContentFields")) - { - await UpgradeAsync(); - } - - // Shortcut other migration steps on new content definition schemas. - return 2; - } + // Shortcut other migration steps on new content definition schemas. + return 2; + } - // Upgrade an existing installation. - private async Task UpgradeAsync() - { - // Boolean field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Upgrade an existing installation. + private async Task UpgradeAsync() + { + // Boolean field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Content picker field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Content picker field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Date field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Date field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Date time field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Date time field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Html field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Html field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Link field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Link field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Localization set content picker field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Localization set content picker field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // MultiText field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // MultiText field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Numeric field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Numeric field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Text field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); - await _contentDefinitionManager.MigrateFieldSettingsAsync(); - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Text field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); + await _contentDefinitionManager.MigrateFieldSettingsAsync(); + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Time field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Time field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // YouTube field - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // YouTube field + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - // Keep in sync the upgrade process. - await UpdateFrom1Async(); - } + // Keep in sync the upgrade process. + await UpdateFrom1Async(); + } - // This step is part of the upgrade process. - public async Task UpdateFrom1Async() + // This step is part of the upgrade process. + public async Task UpdateFrom1Async() + { + // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. + var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); + foreach (var partDefinition in partDefinitions) { - // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. - var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); - foreach (var partDefinition in partDefinitions) + if (partDefinition.Fields.Any(x => x.FieldDefinition.Name == "HtmlField")) { - if (partDefinition.Fields.Any(x => x.FieldDefinition.Name == "HtmlField")) + await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => { - await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => + foreach (var fieldDefinition in partDefinition.Fields.Where(x => x.FieldDefinition.Name == "HtmlField")) { - foreach (var fieldDefinition in partDefinition.Fields.Where(x => x.FieldDefinition.Name == "HtmlField")) + partBuilder.WithField(fieldDefinition.Name, fieldBuilder => { - partBuilder.WithField(fieldDefinition.Name, fieldBuilder => - { - fieldBuilder.MergeSettings(x => x.SanitizeHtml = false); - }); - } - }); - } + fieldBuilder.MergeSettings(x => x.SanitizeHtml = false); + }); + } + }); } - - return 2; } + + return 2; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultContentPickerResultProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultContentPickerResultProvider.cs index 177a5b2b5a2..c21d2104148 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultContentPickerResultProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultContentPickerResultProvider.cs @@ -14,65 +14,64 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentFields.Services +namespace OrchardCore.ContentFields.Services; + +public class DefaultContentPickerResultProvider : IContentPickerResultProvider { - public class DefaultContentPickerResultProvider : IContentPickerResultProvider + private readonly ILiquidTemplateManager _templateManager; + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ISession _session; + + public DefaultContentPickerResultProvider(IContentManager contentManager, IContentDefinitionManager contentDefinitionManager, ISession session, ILiquidTemplateManager templateManager) { - private readonly ILiquidTemplateManager _templateManager; - private readonly IContentManager _contentManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ISession _session; + _templateManager = templateManager; + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _session = session; + } + + public string Name => "Default"; - public DefaultContentPickerResultProvider(IContentManager contentManager, IContentDefinitionManager contentDefinitionManager, ISession session, ILiquidTemplateManager templateManager) + public async Task> Search(ContentPickerSearchContext searchContext) + { + var contentTypes = searchContext.ContentTypes; + if (searchContext.DisplayAllContentTypes) { - _templateManager = templateManager; - _contentManager = contentManager; - _contentDefinitionManager = contentDefinitionManager; - _session = session; + contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(x => !x.HasStereotype()) + .Select(x => x.Name) + .AsEnumerable(); } - public string Name => "Default"; + var query = _session.Query() + .With(x => x.ContentType.IsIn(contentTypes) && x.Latest); - public async Task> Search(ContentPickerSearchContext searchContext) + if (!string.IsNullOrEmpty(searchContext.Query)) { - var contentTypes = searchContext.ContentTypes; - if (searchContext.DisplayAllContentTypes) - { - contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(x => !x.HasStereotype()) - .Select(x => x.Name) - .AsEnumerable(); - } - - var query = _session.Query() - .With(x => x.ContentType.IsIn(contentTypes) && x.Latest); - - if (!string.IsNullOrEmpty(searchContext.Query)) - { - query.With(x => x.DisplayText.Contains(searchContext.Query) || x.ContentType.Contains(searchContext.Query)); - } + query.With(x => x.DisplayText.Contains(searchContext.Query) || x.ContentType.Contains(searchContext.Query)); + } - var contentItems = await query.Take(50).ListAsync(); + var contentItems = await query.Take(50).ListAsync(); - var results = new List(); - var settings = searchContext.PartFieldDefinition.GetSettings(); + var results = new List(); + var settings = searchContext.PartFieldDefinition.GetSettings(); - foreach (var contentItem in contentItems) + foreach (var contentItem in contentItems) + { + var cultureAspect = await _contentManager.PopulateAspectAsync(contentItem, new CultureAspect()); + using (CultureScope.Create(cultureAspect.Culture)) { - var cultureAspect = await _contentManager.PopulateAspectAsync(contentItem, new CultureAspect()); - using (CultureScope.Create(cultureAspect.Culture)) + results.Add(new ContentPickerResult { - results.Add(new ContentPickerResult - { - ContentItemId = contentItem.ContentItemId, - DisplayText = await _templateManager.RenderStringAsync(settings.TitlePattern, NullEncoder.Default, contentItem, - new Dictionary() { [nameof(ContentItem)] = new ObjectValue(contentItem) }), - HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem) - }); - } + ContentItemId = contentItem.ContentItemId, + DisplayText = await _templateManager.RenderStringAsync(settings.TitlePattern, NullEncoder.Default, contentItem, + new Dictionary() { [nameof(ContentItem)] = new ObjectValue(contentItem) }), + HasPublished = await _contentManager.HasPublishedVersionAsync(contentItem) + }); } - - return results.OrderBy(x => x.DisplayText); } + + return results.OrderBy(x => x.DisplayText); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultUserPickerResultProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultUserPickerResultProvider.cs index 52b8ec10b11..d087dff0b30 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultUserPickerResultProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Services/DefaultUserPickerResultProvider.cs @@ -10,56 +10,55 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentFields.Services +namespace OrchardCore.ContentFields.Services; + +public class DefaultUserPickerResultProvider : IUserPickerResultProvider { - public class DefaultUserPickerResultProvider : IUserPickerResultProvider + private readonly RoleManager _roleManager; + private readonly UserManager _userManager; + private readonly ISession _session; + + public DefaultUserPickerResultProvider( + RoleManager roleManager, + UserManager userManager, + ISession session) { - private readonly RoleManager _roleManager; - private readonly UserManager _userManager; - private readonly ISession _session; + _roleManager = roleManager; + _userManager = userManager; + _session = session; + } - public DefaultUserPickerResultProvider( - RoleManager roleManager, - UserManager userManager, - ISession session) - { - _roleManager = roleManager; - _userManager = userManager; - _session = session; - } + public string Name => "Default"; - public string Name => "Default"; + public async Task> Search(UserPickerSearchContext searchContext) + { + var query = _session.Query(); - public async Task> Search(UserPickerSearchContext searchContext) + if (!searchContext.DisplayAllUsers) { - var query = _session.Query(); - - if (!searchContext.DisplayAllUsers) - { - var roles = searchContext.Roles.Select(x => _roleManager.NormalizeKey(x)); - query.With(x => x.RoleName.IsIn(roles)); - } + var roles = searchContext.Roles.Select(x => _roleManager.NormalizeKey(x)); + query.With(x => x.RoleName.IsIn(roles)); + } - if (!string.IsNullOrEmpty(searchContext.Query)) - { - query.With(x => x.NormalizedUserName.Contains(_userManager.NormalizeName(searchContext.Query))); - } + if (!string.IsNullOrEmpty(searchContext.Query)) + { + query.With(x => x.NormalizedUserName.Contains(_userManager.NormalizeName(searchContext.Query))); + } - var users = await query.Take(50).ListAsync(); + var users = await query.Take(50).ListAsync(); - var results = new List(); + var results = new List(); - foreach (var user in users) + foreach (var user in users) + { + results.Add(new UserPickerResult { - results.Add(new UserPickerResult - { - UserId = user.UserId, - DisplayText = user.UserName, - IsEnabled = user.IsEnabled - }); - } - - return results.OrderBy(x => x.DisplayText); + UserId = user.UserId, + DisplayText = user.UserName, + IsEnabled = user.IsEnabled + }); } + + return results.OrderBy(x => x.DisplayText); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettings.cs index e04b8af8041..b3c50bf4045 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class BooleanFieldSettings { - public class BooleanFieldSettings - { - public string Hint { get; set; } - public string Label { get; set; } - public bool DefaultValue { get; set; } - } + public string Hint { get; set; } + public string Label { get; set; } + public bool DefaultValue { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettingsDriver.cs index 31e50dbf579..f2f60a91bf5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/BooleanFieldSettingsDriver.cs @@ -6,31 +6,30 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class BooleanFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class BooleanFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("BooleanFieldSettings_Edit", model => { - return Initialize("BooleanFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Label = settings.Label; - model.DefaultValue = settings.DefaultValue; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Label = settings.Label; + model.DefaultValue = settings.DefaultValue; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new BooleanFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new BooleanFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettings.cs index f691b32863e..cce66912fac 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettings.cs @@ -1,25 +1,24 @@ using System.ComponentModel; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class ContentPickerFieldSettings { - public class ContentPickerFieldSettings - { - public string Hint { get; set; } + public string Hint { get; set; } - public bool Required { get; set; } + public bool Required { get; set; } - public bool Multiple { get; set; } + public bool Multiple { get; set; } - public bool DisplayAllContentTypes { get; set; } + public bool DisplayAllContentTypes { get; set; } - public string[] DisplayedContentTypes { get; set; } = []; + public string[] DisplayedContentTypes { get; set; } = []; - public string[] DisplayedStereotypes { get; set; } = []; + public string[] DisplayedStereotypes { get; set; } = []; - /// - /// The Liquid pattern used to build the title. - /// - [DefaultValue("{{ Model.ContentItem | display_text }}")] - public string TitlePattern { get; set; } = "{{ Model.ContentItem | display_text }}"; - } + /// + /// The Liquid pattern used to build the title. + /// + [DefaultValue("{{ Model.ContentItem | display_text }}")] + public string TitlePattern { get; set; } = "{{ Model.ContentItem | display_text }}"; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettingsDriver.cs index f498e6f94f5..509ba84a224 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/ContentPickerFieldSettingsDriver.cs @@ -10,118 +10,117 @@ using OrchardCore.Liquid; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class ContentPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class ContentPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + private readonly ILiquidTemplateManager _templateManager; + + internal readonly IStringLocalizer S; + + public ContentPickerFieldSettingsDriver( + ILiquidTemplateManager templateManager, + IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } + + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("ContentPickerFieldSettings_Edit", model => + { + var settings = partFieldDefinition.GetSettings(); + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Multiple = settings.Multiple; + model.Source = GetSource(settings); + model.DisplayedContentTypes = settings.DisplayedContentTypes; + model.TitlePattern = settings.TitlePattern; + model.Stereotypes = string.Join(',', settings.DisplayedStereotypes ?? []); + }).Location("Content"); + } + + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) { - private readonly ILiquidTemplateManager _templateManager; + var model = new ContentPickerFieldSettingsViewModel(); - internal readonly IStringLocalizer S; + await context.Updater.TryUpdateModelAsync(model, Prefix); - public ContentPickerFieldSettingsDriver( - ILiquidTemplateManager templateManager, - IStringLocalizer localizer) + var settings = new ContentPickerFieldSettings { - _templateManager = templateManager; - S = localizer; - } + Hint = model.Hint, + Required = model.Required, + Multiple = model.Multiple, + TitlePattern = model.TitlePattern, + }; - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + switch (model.Source) { - return Initialize("ContentPickerFieldSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Multiple = settings.Multiple; - model.Source = GetSource(settings); - model.DisplayedContentTypes = settings.DisplayedContentTypes; - model.TitlePattern = settings.TitlePattern; - model.Stereotypes = string.Join(',', settings.DisplayedStereotypes ?? []); - }).Location("Content"); + case ContentPickerSettingType.ContentTypes: + SetContentTypes(context.Updater, model.DisplayedContentTypes, settings); + break; + case ContentPickerSettingType.Stereotypes: + SetStereoTypes(context.Updater, model.Stereotypes, settings); + break; + default: + settings.DisplayAllContentTypes = true; + break; } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + if (IsValidTitlePattern(context, model)) { - var model = new ContentPickerFieldSettingsViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - - var settings = new ContentPickerFieldSettings - { - Hint = model.Hint, - Required = model.Required, - Multiple = model.Multiple, - TitlePattern = model.TitlePattern, - }; - - switch (model.Source) - { - case ContentPickerSettingType.ContentTypes: - SetContentTypes(context.Updater, model.DisplayedContentTypes, settings); - break; - case ContentPickerSettingType.Stereotypes: - SetStereoTypes(context.Updater, model.Stereotypes, settings); - break; - default: - settings.DisplayAllContentTypes = true; - break; - } - - if (IsValidTitlePattern(context, model)) - { - context.Builder.WithSettings(settings); - } - - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(settings); } - private bool IsValidTitlePattern(UpdatePartFieldEditorContext context, ContentPickerFieldSettingsViewModel model) - { - if (!string.IsNullOrEmpty(model.TitlePattern) && !_templateManager.Validate(model.TitlePattern, out var titleErrors)) - { - context.Updater.ModelState.AddModelError(nameof(model.TitlePattern), S["Title Pattern does not contain a valid Liquid expression. Details: {0}", string.Join(" ", titleErrors)]); + return Edit(partFieldDefinition, context); + } - return false; - } + private bool IsValidTitlePattern(UpdatePartFieldEditorContext context, ContentPickerFieldSettingsViewModel model) + { + if (!string.IsNullOrEmpty(model.TitlePattern) && !_templateManager.Validate(model.TitlePattern, out var titleErrors)) + { + context.Updater.ModelState.AddModelError(nameof(model.TitlePattern), S["Title Pattern does not contain a valid Liquid expression. Details: {0}", string.Join(" ", titleErrors)]); - return true; + return false; } - private void SetStereoTypes(IUpdateModel updater, string stereotypes, ContentPickerFieldSettings settings) - { - if (string.IsNullOrEmpty(stereotypes)) - { - updater.ModelState.AddModelError(Prefix, nameof(ContentPickerFieldSettingsViewModel.Stereotypes), S["Please provide a Stereotype."]); + return true; + } - return; - } + private void SetStereoTypes(IUpdateModel updater, string stereotypes, ContentPickerFieldSettings settings) + { + if (string.IsNullOrEmpty(stereotypes)) + { + updater.ModelState.AddModelError(Prefix, nameof(ContentPickerFieldSettingsViewModel.Stereotypes), S["Please provide a Stereotype."]); - settings.DisplayedStereotypes = stereotypes.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + return; } - private void SetContentTypes(IUpdateModel updater, string[] displayedContentTypes, ContentPickerFieldSettings settings) - { - if (displayedContentTypes == null || displayedContentTypes.Length == 0) - { - updater.ModelState.AddModelError(Prefix, nameof(ContentPickerFieldSettingsViewModel.DisplayedContentTypes), S["At least one content type must be selected."]); + settings.DisplayedStereotypes = stereotypes.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + } - return; - } + private void SetContentTypes(IUpdateModel updater, string[] displayedContentTypes, ContentPickerFieldSettings settings) + { + if (displayedContentTypes == null || displayedContentTypes.Length == 0) + { + updater.ModelState.AddModelError(Prefix, nameof(ContentPickerFieldSettingsViewModel.DisplayedContentTypes), S["At least one content type must be selected."]); - settings.DisplayedContentTypes = displayedContentTypes; + return; } - private static ContentPickerSettingType GetSource(ContentPickerFieldSettings settings) + settings.DisplayedContentTypes = displayedContentTypes; + } + + private static ContentPickerSettingType GetSource(ContentPickerFieldSettings settings) + { + if (settings.DisplayAllContentTypes) { - if (settings.DisplayAllContentTypes) - { - return ContentPickerSettingType.AllTypes; - } - - return settings.DisplayedStereotypes != null && settings.DisplayedStereotypes.Length > 0 - ? ContentPickerSettingType.Stereotypes - : ContentPickerSettingType.ContentTypes; + return ContentPickerSettingType.AllTypes; } + + return settings.DisplayedStereotypes != null && settings.DisplayedStereotypes.Length > 0 + ? ContentPickerSettingType.Stereotypes + : ContentPickerSettingType.ContentTypes; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettings.cs index e80656fa838..ed0697fab37 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class DateFieldSettings { - public class DateFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettingsDriver.cs index e84d2d11723..3032a54a3c1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateFieldSettingsDriver.cs @@ -6,30 +6,29 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class DateFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class DateFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("DateFieldSettings_Edit", model => { - return Initialize("DateFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new DateFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new DateFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettings.cs index 9d55cf53645..f41a39a8c03 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class DateTimeFieldSettings { - public class DateTimeFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettingsDriver.cs index 4870baef12f..aafc3d0e665 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/DateTimeFieldSettingsDriver.cs @@ -6,30 +6,29 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class DateTimeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class DateTimeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("DateTimeFieldSettings_Edit", model => { - return Initialize("DateTimeFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new DateTimeFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new DateTimeFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettings.cs index 1ad252aab0b..c6e753d8a9a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class HtmlFieldMonacoEditorSettings { - public class HtmlFieldMonacoEditorSettings - { - public string Options { get; set; } - } + public string Options { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettingsDriver.cs index 576e5c602d3..177ca835061 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldMonacoEditorSettingsDriver.cs @@ -11,60 +11,59 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class HtmlFieldMonacoEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class HtmlFieldMonacoEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public HtmlFieldMonacoEditorSettingsDriver(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public HtmlFieldMonacoEditorSettingsDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("HtmlFieldMonacoEditorSettings_Edit", model => { - return Initialize("HtmlFieldMonacoEditorSettings_Edit", model => + var settings = partFieldDefinition.GetSettings(); + if (string.IsNullOrWhiteSpace(settings.Options)) { - var settings = partFieldDefinition.GetSettings(); - if (string.IsNullOrWhiteSpace(settings.Options)) + settings.Options = JConvert.SerializeObject(new { - settings.Options = JConvert.SerializeObject(new - { - automaticLayout = true, - language = "html" - }, JOptions.Indented); - } + automaticLayout = true, + language = "html" + }, JOptions.Indented); + } - model.Options = settings.Options; - }).Location("Editor"); - } + model.Options = settings.Options; + }).Location("Editor"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Monaco") { - if (partFieldDefinition.Editor() == "Monaco") - { - var model = new MonacoSettingsViewModel(); + var model = new MonacoSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (!model.Options.IsJson()) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); - } - else + if (!model.Options.IsJson()) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); + } + else + { + var jsonSettings = JObject.Parse(model.Options); + jsonSettings["language"] = "html"; + var settings = new HtmlFieldMonacoEditorSettings { - var jsonSettings = JObject.Parse(model.Options); - jsonSettings["language"] = "html"; - var settings = new HtmlFieldMonacoEditorSettings - { - Options = jsonSettings.ToString() - }; - context.Builder.WithSettings(settings); - } + Options = jsonSettings.ToString() + }; + context.Builder.WithSettings(settings); } - - return Edit(partFieldDefinition, context); } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettings.cs index 745d8f661ef..275a35a6e39 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettings.cs @@ -1,12 +1,11 @@ using System.ComponentModel; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class HtmlFieldSettings { - public class HtmlFieldSettings - { - public string Hint { get; set; } + public string Hint { get; set; } - [DefaultValue(true)] - public bool SanitizeHtml { get; set; } = true; - } + [DefaultValue(true)] + public bool SanitizeHtml { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettingsDriver.cs index 79f14be5b37..be249e8df2a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldSettingsDriver.cs @@ -6,34 +6,33 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class HtmlFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class HtmlFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("HtmlFieldSettings_Edit", model => { - return Initialize("HtmlFieldSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var settings = partFieldDefinition.GetSettings(); - model.SanitizeHtml = settings.SanitizeHtml; - model.Hint = settings.Hint; - }).Location("Content:20"); - } + model.SanitizeHtml = settings.SanitizeHtml; + model.Hint = settings.Hint; + }).Location("Content:20"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new HtmlSettingsViewModel(); - var settings = new HtmlFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new HtmlSettingsViewModel(); + var settings = new HtmlFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SanitizeHtml = model.SanitizeHtml; - settings.Hint = model.Hint; + settings.SanitizeHtml = model.SanitizeHtml; + settings.Hint = model.Hint; - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettings.cs index 7ab2def05eb..1de660c4fcf 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class HtmlFieldTrumbowygEditorSettings { - public class HtmlFieldTrumbowygEditorSettings - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettingsDriver.cs index 4386632cd62..f6f1076537f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/HtmlFieldTrumbowygEditorSettingsDriver.cs @@ -10,64 +10,63 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class HtmlFieldTrumbowygEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class HtmlFieldTrumbowygEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver + internal readonly IStringLocalizer S; + + public HtmlFieldTrumbowygEditorSettingsDriver(IStringLocalizer stringLocalizer) { - internal readonly IStringLocalizer S; + S = stringLocalizer; + } - public HtmlFieldTrumbowygEditorSettingsDriver(IStringLocalizer stringLocalizer) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("HtmlFieldTrumbowygEditorSettings_Edit", model => { - S = stringLocalizer; - } + var settings = partFieldDefinition.GetSettings(); - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + model.Options = settings.Options; + model.InsertMediaWithUrl = settings.InsertMediaWithUrl; + }).Location("Editor"); + } + + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Trumbowyg") { - return Initialize("HtmlFieldTrumbowygEditorSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var model = new TrumbowygSettingsViewModel(); - model.Options = settings.Options; - model.InsertMediaWithUrl = settings.InsertMediaWithUrl; - }).Location("Editor"); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - if (partFieldDefinition.Editor() == "Trumbowyg") + try { - var model = new TrumbowygSettingsViewModel(); + var options = model.Options.Trim(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - try + if (!options.StartsWith('{') || !options.EndsWith('}')) { - var options = model.Options.Trim(); - - if (!options.StartsWith('{') || !options.EndsWith('}')) - { - throw new Exception(); - } - - var parser = new Parser(); + throw new Exception(); + } - var optionsScript = parser.ParseScript("var config = " + options); + var parser = new Parser(); - var settings = new HtmlFieldTrumbowygEditorSettings - { - InsertMediaWithUrl = model.InsertMediaWithUrl, - Options = options - }; + var optionsScript = parser.ParseScript("var config = " + options); - context.Builder.WithSettings(settings); - } - catch + var settings = new HtmlFieldTrumbowygEditorSettings { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); - } - } + InsertMediaWithUrl = model.InsertMediaWithUrl, + Options = options + }; - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(settings); + } + catch + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); + } } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettings.cs index 4b0ad29b19a..6aa42fe93b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettings.cs @@ -1,35 +1,34 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class LinkFieldSettings { - public class LinkFieldSettings - { - public string Hint { get; set; } - public string HintLinkText { get; set; } - public bool Required { get; set; } - public LinkTextMode LinkTextMode { get; set; } - public string UrlPlaceholder { get; set; } - public string TextPlaceholder { get; set; } - public string DefaultUrl { get; set; } - public string DefaultText { get; set; } - public string DefaultTarget { get; set; } + public string Hint { get; set; } + public string HintLinkText { get; set; } + public bool Required { get; set; } + public LinkTextMode LinkTextMode { get; set; } + public string UrlPlaceholder { get; set; } + public string TextPlaceholder { get; set; } + public string DefaultUrl { get; set; } + public string DefaultText { get; set; } + public string DefaultTarget { get; set; } - public LinkFieldSettings() - { - LinkTextMode = LinkTextMode.Optional; - } + public LinkFieldSettings() + { + LinkTextMode = LinkTextMode.Optional; } +} - public enum LinkTextMode - { - // Some text can be entered or not, if not the url is used - Optional, +public enum LinkTextMode +{ + // Some text can be entered or not, if not the url is used + Optional, - // Some text must be entered - Required, + // Some text must be entered + Required, - // Use the default text value defined in the settings - Static, + // Use the default text value defined in the settings + Static, - // Use the url - Url - } + // Use the url + Url } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettingsDriver.cs index 3c00c56d54e..e7ab6b8c18a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LinkFieldSettingsDriver.cs @@ -6,37 +6,36 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class LinkFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class LinkFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("LinkFieldSettings_Edit", model => { - return Initialize("LinkFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.HintLinkText = settings.HintLinkText; - model.Required = settings.Required; - model.LinkTextMode = settings.LinkTextMode; - model.UrlPlaceholder = settings.UrlPlaceholder; - model.TextPlaceholder = settings.TextPlaceholder; - model.DefaultUrl = settings.DefaultUrl; - model.DefaultText = settings.DefaultText; - model.DefaultTarget = settings.DefaultTarget; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.HintLinkText = settings.HintLinkText; + model.Required = settings.Required; + model.LinkTextMode = settings.LinkTextMode; + model.UrlPlaceholder = settings.UrlPlaceholder; + model.TextPlaceholder = settings.TextPlaceholder; + model.DefaultUrl = settings.DefaultUrl; + model.DefaultText = settings.DefaultText; + model.DefaultTarget = settings.DefaultTarget; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new LinkFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new LinkFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettings.cs index 8d324ca82a4..c9b37f2dfaf 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettings.cs @@ -1,13 +1,12 @@ using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +[RequireFeatures("OrchardCore.ContentLocalization")] +public class LocalizationSetContentPickerFieldSettings { - [RequireFeatures("OrchardCore.ContentLocalization")] - public class LocalizationSetContentPickerFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public bool Multiple { get; set; } - public string[] DisplayedContentTypes { get; set; } = []; - } + public string Hint { get; set; } + public bool Required { get; set; } + public bool Multiple { get; set; } + public string[] DisplayedContentTypes { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettingsDriver.cs index fa5e01882ca..b2c306bb712 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/LocalizationSetContentPickerFieldSettingsDriver.cs @@ -7,33 +7,32 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Modules; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +[RequireFeatures("OrchardCore.ContentLocalization")] +public sealed class LocalizationSetContentPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - [RequireFeatures("OrchardCore.ContentLocalization")] - public sealed class LocalizationSetContentPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("LocalizationSetContentPickerFieldSettings_Edit", model => { - return Initialize("LocalizationSetContentPickerFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Multiple = settings.Multiple; - model.DisplayedContentTypes = settings.DisplayedContentTypes; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Multiple = settings.Multiple; + model.DisplayedContentTypes = settings.DisplayedContentTypes; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new LocalizationSetContentPickerFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new LocalizationSetContentPickerFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettings.cs index 99b5188dc96..24358b97f2a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettings.cs @@ -1,23 +1,22 @@ using System.Text.Json.Serialization; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class MultiTextFieldSettings { - public class MultiTextFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public MultiTextFieldValueOption[] Options { get; set; } = []; - } + public string Hint { get; set; } + public bool Required { get; set; } + public MultiTextFieldValueOption[] Options { get; set; } = []; +} - public class MultiTextFieldValueOption - { - [JsonPropertyName("name")] - public string Name { get; set; } +public class MultiTextFieldValueOption +{ + [JsonPropertyName("name")] + public string Name { get; set; } - [JsonPropertyName("value")] - public string Value { get; set; } + [JsonPropertyName("value")] + public string Value { get; set; } - [JsonPropertyName("default")] - public bool Default { get; set; } - } + [JsonPropertyName("default")] + public bool Default { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettingsDriver.cs index 27c022351ce..0e6d2cfa295 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/MultiTextFieldSettingsDriver.cs @@ -8,51 +8,50 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class MultiTextFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class MultiTextFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public MultiTextFieldSettingsDriver(IStringLocalizer localizer) - { - S = localizer; - } + public MultiTextFieldSettingsDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("MultiTextFieldSettings_Edit", model => { - return Initialize("MultiTextFieldSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); - - model.Required = settings.Required; - model.Hint = settings.Hint; - model.Options = JConvert.SerializeObject(settings.Options, JOptions.Indented); - }).Location("Content"); - } + var settings = partFieldDefinition.GetSettings(); - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new MultiTextFieldSettingsViewModel(); - var settings = new MultiTextFieldSettings(); + model.Required = settings.Required; + model.Hint = settings.Hint; + model.Options = JConvert.SerializeObject(settings.Options, JOptions.Indented); + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new MultiTextFieldSettingsViewModel(); + var settings = new MultiTextFieldSettings(); - settings.Required = model.Required; - settings.Hint = model.Hint; + await context.Updater.TryUpdateModelAsync(model, Prefix); - try - { - settings.Options = JConvert.DeserializeObject(model.Options); + settings.Required = model.Required; + settings.Hint = model.Hint; - context.Builder.WithSettings(settings); - } - catch - { - context.Updater.ModelState.AddModelError(Prefix, S["The options are written in an incorrect format."]); - } + try + { + settings.Options = JConvert.DeserializeObject(model.Options); - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(settings); } + catch + { + context.Updater.ModelState.AddModelError(Prefix, S["The options are written in an incorrect format."]); + } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettings.cs index adcf7fd7da8..ce8426d06d2 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettings.cs @@ -1,13 +1,12 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class NumericFieldSettings { - public class NumericFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public int Scale { get; set; } - public decimal? Minimum { get; set; } - public decimal? Maximum { get; set; } - public string Placeholder { get; set; } - public string DefaultValue { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } + public int Scale { get; set; } + public decimal? Minimum { get; set; } + public decimal? Maximum { get; set; } + public string Placeholder { get; set; } + public string DefaultValue { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettingsDriver.cs index bbdafea4558..428d865a4dd 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/NumericFieldSettingsDriver.cs @@ -6,35 +6,34 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class NumericFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class NumericFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("NumericFieldSettings_Edit", model => { - return Initialize("NumericFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Scale = settings.Scale; - model.Minimum = settings.Minimum; - model.Maximum = settings.Maximum; - model.Placeholder = settings.Placeholder; - model.DefaultValue = settings.DefaultValue; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Scale = settings.Scale; + model.Minimum = settings.Minimum; + model.Maximum = settings.Maximum; + model.Placeholder = settings.Placeholder; + model.DefaultValue = settings.DefaultValue; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new NumericFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new NumericFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettings.cs index 8c66e528ffa..2f00870f8f5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class TextFieldHeaderDisplaySettings { - public class TextFieldHeaderDisplaySettings - { - public string Level { get; set; } - } + public string Level { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettingsDriver.cs index 78189d743f5..d14413147be 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldHeaderDisplaySettingsDriver.cs @@ -6,35 +6,34 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class TextFieldHeaderDisplaySettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TextFieldHeaderDisplaySettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("TextFieldHeaderDisplaySettings_Edit", model => { - return Initialize("TextFieldHeaderDisplaySettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var settings = partFieldDefinition.GetSettings(); - model.Level = settings.Level; - }).Location("DisplayMode"); - } + model.Level = settings.Level; + }).Location("DisplayMode"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.DisplayMode() == "Header") { - if (partFieldDefinition.DisplayMode() == "Header") - { - var model = new HeaderSettingsViewModel(); - var settings = new TextFieldHeaderDisplaySettings(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); + var model = new HeaderSettingsViewModel(); + var settings = new TextFieldHeaderDisplaySettings(); - settings.Level = model.Level; + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(settings); - } + settings.Level = model.Level; - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(settings); } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettings.cs index a38f0f4de20..ae742453f86 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class TextFieldMonacoEditorSettings { - public class TextFieldMonacoEditorSettings - { - public string Options { get; set; } - } + public string Options { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettingsDriver.cs index fa1a7546eeb..ec6c0818a63 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldMonacoEditorSettingsDriver.cs @@ -10,54 +10,53 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class TextFieldMonacoEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TextFieldMonacoEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public TextFieldMonacoEditorSettingsDriver(IStringLocalizer localizer) - { - S = localizer; - } + public TextFieldMonacoEditorSettingsDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("TextFieldMonacoEditorSettings_Edit", model => { - return Initialize("TextFieldMonacoEditorSettings_Edit", model => + var settings = partFieldDefinition.GetSettings(); + if (string.IsNullOrWhiteSpace(settings.Options)) { - var settings = partFieldDefinition.GetSettings(); - if (string.IsNullOrWhiteSpace(settings.Options)) - { - settings.Options = JConvert.SerializeObject(new { automaticLayout = true, language = "html" }, JOptions.Indented); - } + settings.Options = JConvert.SerializeObject(new { automaticLayout = true, language = "html" }, JOptions.Indented); + } - model.Options = settings.Options; - }).Location("Editor"); - } + model.Options = settings.Options; + }).Location("Editor"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Monaco") { - if (partFieldDefinition.Editor() == "Monaco") - { - var model = new MonacoSettingsViewModel(); + var model = new MonacoSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (!model.Options.IsJson()) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); - } - else + if (!model.Options.IsJson()) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); + } + else + { + var settings = new TextFieldMonacoEditorSettings { - var settings = new TextFieldMonacoEditorSettings - { - Options = model.Options - }; - context.Builder.WithSettings(settings); - } + Options = model.Options + }; + context.Builder.WithSettings(settings); } - - return Edit(partFieldDefinition, context); } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettings.cs index e67d9b42496..6489323fa80 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettings.cs @@ -1,40 +1,39 @@ using System.Text.Json.Serialization; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class TextFieldPredefinedListEditorSettings +{ + public ListValueOption[] Options { get; set; } + public EditorOption Editor { get; set; } + public string DefaultValue { get; set; } +} + +public enum EditorOption +{ + Radio, + Dropdown +} + +public class ListValueOption { - public class TextFieldPredefinedListEditorSettings + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("value")] + public string Value { get; set; } + + public ListValueOption() { - public ListValueOption[] Options { get; set; } - public EditorOption Editor { get; set; } - public string DefaultValue { get; set; } } - public enum EditorOption + public ListValueOption(string name) : this(name, name) { - Radio, - Dropdown } - public class ListValueOption + public ListValueOption(string name, string value) { - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("value")] - public string Value { get; set; } - - public ListValueOption() - { - } - - public ListValueOption(string name) : this(name, name) - { - } - - public ListValueOption(string name, string value) - { - Name = name; - Value = value; - } + Name = name; + Value = value; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettingsDriver.cs index 7d76a254f6d..b306344d682 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldPredefinedListEditorSettingsDriver.cs @@ -8,55 +8,54 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class TextFieldPredefinedListEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TextFieldPredefinedListEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver + internal readonly IStringLocalizer S; + + public TextFieldPredefinedListEditorSettingsDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public TextFieldPredefinedListEditorSettingsDriver(IStringLocalizer localizer) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("TextFieldPredefinedListEditorSettings_Edit", model => { - S = localizer; - } + var settings = partFieldDefinition.GetSettings(); + + model.DefaultValue = settings.DefaultValue; + model.Editor = settings.Editor; + model.Options = JConvert.SerializeObject(settings.Options ?? [], JOptions.Indented); + }).Location("Editor"); + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "PredefinedList") { - return Initialize("TextFieldPredefinedListEditorSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var model = new PredefinedListSettingsViewModel(); + var settings = new TextFieldPredefinedListEditorSettings(); - model.DefaultValue = settings.DefaultValue; - model.Editor = settings.Editor; - model.Options = JConvert.SerializeObject(settings.Options ?? [], JOptions.Indented); - }).Location("Editor"); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - if (partFieldDefinition.Editor() == "PredefinedList") + try { - var model = new PredefinedListSettingsViewModel(); - var settings = new TextFieldPredefinedListEditorSettings(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - - try - { - settings.DefaultValue = model.DefaultValue; - settings.Editor = model.Editor; - settings.Options = string.IsNullOrWhiteSpace(model.Options) - ? [] - : JConvert.DeserializeObject(model.Options); - - context.Builder.WithSettings(settings); - } - catch - { - context.Updater.ModelState.AddModelError(Prefix, S["The options are written in an incorrect format."]); - } - } + settings.DefaultValue = model.DefaultValue; + settings.Editor = model.Editor; + settings.Options = string.IsNullOrWhiteSpace(model.Options) + ? [] + : JConvert.DeserializeObject(model.Options); - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(settings); + } + catch + { + context.Updater.ModelState.AddModelError(Prefix, S["The options are written in an incorrect format."]); + } } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettings.cs index 4710ef1760e..bb8880c019f 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class TextFieldSettings { - public class TextFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public string DefaultValue { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } + public string DefaultValue { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettingsDriver.cs index dce839956a8..22e1349c7a5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TextFieldSettingsDriver.cs @@ -6,31 +6,30 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class TextFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TextFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("TextFieldSettings_Edit", model => { - return Initialize("TextFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.DefaultValue = settings.DefaultValue; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + model.DefaultValue = settings.DefaultValue; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new TextFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new TextFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettings.cs index 208e8ffe9b3..6be1d9109e8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class TimeFieldSettings { - public class TimeFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public string Step { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } + public string Step { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettingsDriver.cs index eaf88e247e2..60373632566 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/TimeFieldSettingsDriver.cs @@ -6,31 +6,30 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class TimeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TimeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("TimeFieldSettings_Edit", model => { - return Initialize("TimeFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Step = settings.Step; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Step = settings.Step; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new TimeFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new TimeFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettings.cs index c59a3f72ecf..15d205ae57b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettings.cs @@ -1,15 +1,14 @@ using System.ComponentModel; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class UserPickerFieldSettings { - public class UserPickerFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - public bool Multiple { get; set; } + public string Hint { get; set; } + public bool Required { get; set; } + public bool Multiple { get; set; } - [DefaultValue(true)] - public bool DisplayAllUsers { get; set; } = true; - public string[] DisplayedRoles { get; set; } = []; - } + [DefaultValue(true)] + public bool DisplayAllUsers { get; set; } = true; + public string[] DisplayedRoles { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsDriver.cs index a7aad0133b7..c66b7f460f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsDriver.cs @@ -8,69 +8,68 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Security.Services; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class UserPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class UserPickerFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - private readonly IRoleService _roleService; + private readonly IRoleService _roleService; - public UserPickerFieldSettingsDriver(IRoleService roleService) - { - _roleService = roleService; - } + public UserPickerFieldSettingsDriver(IRoleService roleService) + { + _roleService = roleService; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("UserPickerFieldSettings_Edit", async model => { - return Initialize("UserPickerFieldSettings_Edit", async model => - { - var settings = partFieldDefinition.GetSettings(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Multiple = settings.Multiple; - var roles = (await _roleService.GetRoleNamesAsync()) - .Except(RoleHelper.SystemRoleNames, StringComparer.OrdinalIgnoreCase) - .Select(roleName => new RoleEntry - { - Role = roleName, - IsSelected = settings.DisplayedRoles.Contains(roleName, StringComparer.OrdinalIgnoreCase) - }) - .ToArray(); + var settings = partFieldDefinition.GetSettings(); + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Multiple = settings.Multiple; + var roles = (await _roleService.GetRoleNamesAsync()) + .Except(RoleHelper.SystemRoleNames, StringComparer.OrdinalIgnoreCase) + .Select(roleName => new RoleEntry + { + Role = roleName, + IsSelected = settings.DisplayedRoles.Contains(roleName, StringComparer.OrdinalIgnoreCase) + }) + .ToArray(); - model.Roles = roles; - model.DisplayAllUsers = settings.DisplayAllUsers || !roles.Where(x => x.IsSelected).Any(); - }).Location("Content"); - } + model.Roles = roles; + model.DisplayAllUsers = settings.DisplayAllUsers || !roles.Where(x => x.IsSelected).Any(); + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new UserPickerFieldSettingsViewModel(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new UserPickerFieldSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - var settings = new UserPickerFieldSettings - { - Hint = model.Hint, - Required = model.Required, - Multiple = model.Multiple - }; + var settings = new UserPickerFieldSettings + { + Hint = model.Hint, + Required = model.Required, + Multiple = model.Multiple + }; - var selectedRoles = model.Roles.Where(x => x.IsSelected).Select(x => x.Role).ToArray(); + var selectedRoles = model.Roles.Where(x => x.IsSelected).Select(x => x.Role).ToArray(); - if (model.DisplayAllUsers || selectedRoles.Length == 0) - { - // No selected role should have the same effect as display all users - settings.DisplayedRoles = []; - settings.DisplayAllUsers = true; - } - else - { - settings.DisplayedRoles = selectedRoles; - settings.DisplayAllUsers = false; - } + if (model.DisplayAllUsers || selectedRoles.Length == 0) + { + // No selected role should have the same effect as display all users + settings.DisplayedRoles = []; + settings.DisplayAllUsers = true; + } + else + { + settings.DisplayedRoles = selectedRoles; + settings.DisplayAllUsers = false; + } - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsViewModel.cs index dfbf56e9b8f..92b531d6bd5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/UserPickerFieldSettingsViewModel.cs @@ -1,17 +1,16 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class UserPickerFieldSettingsViewModel { - public class UserPickerFieldSettingsViewModel - { - public string Hint { get; set; } - public bool Required { get; set; } - public bool Multiple { get; set; } - public bool DisplayAllUsers { get; set; } - public RoleEntry[] Roles { get; set; } = []; - } + public string Hint { get; set; } + public bool Required { get; set; } + public bool Multiple { get; set; } + public bool DisplayAllUsers { get; set; } + public RoleEntry[] Roles { get; set; } = []; +} - public class RoleEntry - { - public string Role { get; set; } - public bool IsSelected { get; set; } - } +public class RoleEntry +{ + public string Role { get; set; } + public bool IsSelected { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettings.cs index dd0c8420545..32cfbc049a0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettings.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public class YoutubeFieldSettings { - public class YoutubeFieldSettings - { - public string Hint { get; set; } - public string Label { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public bool Required { get; set; } - } + public string Hint { get; set; } + public string Label { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public bool Required { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettingsDriver.cs index e0875cdaab0..7e7eceaff39 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Settings/YoutubeFieldSettingsDriver.cs @@ -6,29 +6,28 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentFields.Settings +namespace OrchardCore.ContentFields.Settings; + +public sealed class YoutubeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class YoutubeFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("YoutubeFieldSetting_Edit", model => { - return Initialize("YoutubeFieldSetting_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Height = model.Height != default ? model.Height : 315; - model.Width = model.Width != default ? model.Width : 560; - }).Location("Content"); - } + model.Height = model.Height != default ? model.Height : 315; + model.Width = model.Width != default ? model.Width : 560; + }).Location("Content"); + } - public async override Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new YoutubeFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + public async override Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new YoutubeFieldSettings(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/Startup.cs index 1febce56fd9..4b5db23739a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/Startup.cs @@ -20,197 +20,196 @@ using OrchardCore.ResourceManagement; using OrchardCore.Users; -namespace OrchardCore.ContentFields +namespace OrchardCore.ContentFields; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.AddTransient, ResourceManagementOptionsConfiguration>(); + + services.Configure(o => { - services.AddTransient, ResourceManagementOptionsConfiguration>(); - - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - - // Boolean Field - services.AddContentField() - .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); - - // Text Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Html Field - services.AddContentField() - .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Link Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // MultiText Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // Numeric Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // DateTime Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // Date Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // Time Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // Video field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - - // Content picker field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Migration, can be removed in a future release. - services.AddDataMigration(); - } + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); + + // Boolean Field + services.AddContentField() + .UseDisplayDriver(); + services.AddScoped(); + services.AddScoped(); + + // Text Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Html Field + services.AddContentField() + .UseDisplayDriver(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Link Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // MultiText Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // Numeric Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // DateTime Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // Date Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // Time Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // Video field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + + // Content picker field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Migration, can be removed in a future release. + services.AddDataMigration(); } +} - [RequireFeatures("OrchardCore.ContentLocalization")] - public sealed class LocalizationSetContentPickerStartup : StartupBase +[RequireFeatures("OrchardCore.ContentLocalization")] +public sealed class LocalizationSetContentPickerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } +} - [Feature("OrchardCore.ContentFields.Indexing.SQL")] - public sealed class IndexingStartup : StartupBase +[Feature("OrchardCore.ContentFields.Indexing.SQL")] +public sealed class IndexingStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - services.AddScopedIndexProvider(); - } + services.AddDataMigration(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); } +} - [RequireFeatures(UserConstants.Features.Users)] - public sealed class UserPickerStartup : StartupBase +[RequireFeatures(UserConstants.Features.Users)] +public sealed class UserPickerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - - services.AddContentField() - .UseDisplayDriver(d => !string.Equals(d, "UserNames", StringComparison.OrdinalIgnoreCase)) - .UseDisplayDriver(d => string.Equals(d, "UserNames", StringComparison.OrdinalIgnoreCase)) - .AddHandler(); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - } + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); + + services.AddContentField() + .UseDisplayDriver(d => !string.Equals(d, "UserNames", StringComparison.OrdinalIgnoreCase)) + .UseDisplayDriver(d => string.Equals(d, "UserNames", StringComparison.OrdinalIgnoreCase)) + .AddHandler(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } +} - [Feature("OrchardCore.ContentFields.Indexing.SQL.UserPicker")] - public sealed class UserPickerSqlIndexingStartup : StartupBase +[Feature("OrchardCore.ContentFields.Indexing.SQL.UserPicker")] +public sealed class UserPickerSqlIndexingStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddScopedIndexProvider(); - } + services.AddDataMigration(); + services.AddScopedIndexProvider(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayBooleanFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayBooleanFieldViewModel.cs index 068d7b14a9c..6b30159dcce 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayBooleanFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayBooleanFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayBooleanFieldViewModel { - public class DisplayBooleanFieldViewModel - { - public bool Value => Field.Value; - public BooleanField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public bool Value => Field.Value; + public BooleanField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayContentPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayContentPickerFieldViewModel.cs index 5d5249a17cb..923300cf541 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayContentPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayContentPickerFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayContentPickerFieldViewModel { - public class DisplayContentPickerFieldViewModel - { - public string[] ContentItemIds => Field.ContentItemIds; - public ContentPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] ContentItemIds => Field.ContentItemIds; + public ContentPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateFieldViewModel.cs index de09b9b4bb1..a9ad7b89dc3 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateFieldViewModel.cs @@ -3,13 +3,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayDateFieldViewModel { - public class DisplayDateFieldViewModel - { - public DateTime? Value => Field.Value; - public DateField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public DateTime? Value => Field.Value; + public DateField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateTimeFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateTimeFieldViewModel.cs index b26757896a8..5f7629ce321 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateTimeFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayDateTimeFieldViewModel.cs @@ -3,14 +3,13 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayDateTimeFieldViewModel { - public class DisplayDateTimeFieldViewModel - { - public DateTime? Value => Field.Value; - public DateTime? LocalDateTime { get; set; } - public DateTimeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public DateTime? Value => Field.Value; + public DateTime? LocalDateTime { get; set; } + public DateTimeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayHtmlFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayHtmlFieldViewModel.cs index ebf18c67f5c..fc6d9202976 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayHtmlFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayHtmlFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayHtmlFieldViewModel { - public class DisplayHtmlFieldViewModel - { - public string Html { get; set; } - public HtmlField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Html { get; set; } + public HtmlField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLinkFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLinkFieldViewModel.cs index dce277ef9cb..ce0646619b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLinkFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLinkFieldViewModel.cs @@ -2,15 +2,14 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayLinkFieldViewModel { - public class DisplayLinkFieldViewModel - { - public string Url => Field.Url; - public string Text => Field.Text; - public string Target => Field.Target; - public LinkField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Url => Field.Url; + public string Text => Field.Text; + public string Target => Field.Target; + public LinkField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLocalizationSetContentPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLocalizationSetContentPickerFieldViewModel.cs index 273d8acd01f..82f2685c255 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLocalizationSetContentPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayLocalizationSetContentPickerFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayLocalizationSetContentPickerFieldViewModel { - public class DisplayLocalizationSetContentPickerFieldViewModel - { - public string[] LocalizationSets => Field.LocalizationSets; - public LocalizationSetContentPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] LocalizationSets => Field.LocalizationSets; + public LocalizationSetContentPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayMultiTextFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayMultiTextFieldViewModel.cs index 5b8b585a37a..22b1237133b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayMultiTextFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayMultiTextFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayMultiTextFieldViewModel { - public class DisplayMultiTextFieldViewModel - { - public string[] Values { get; set; } - public MultiTextField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] Values { get; set; } + public MultiTextField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayNumericFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayNumericFieldViewModel.cs index 4eb2e9c115f..26c157d0cca 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayNumericFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayNumericFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayNumericFieldViewModel { - public class DisplayNumericFieldViewModel - { - public decimal? Value => Field.Value; - public NumericField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public decimal? Value => Field.Value; + public NumericField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTextFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTextFieldViewModel.cs index 5716f6eab21..2c4ae5065ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTextFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTextFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayTextFieldViewModel { - public class DisplayTextFieldViewModel - { - public string Text => Field.Text; - public TextField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Text => Field.Text; + public TextField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTimeFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTimeFieldViewModel.cs index 32ef25d0a85..c0326f71d46 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTimeFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayTimeFieldViewModel.cs @@ -3,13 +3,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayTimeFieldViewModel { - public class DisplayTimeFieldViewModel - { - public TimeSpan? Value => Field.Value; - public TimeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public TimeSpan? Value => Field.Value; + public TimeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldUserNamesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldUserNamesViewModel.cs index 2d111eaac12..376c7ef14bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldUserNamesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldUserNamesViewModel.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayUserPickerFieldUserNamesViewModel { - public class DisplayUserPickerFieldUserNamesViewModel - { - public string[] UserIds => Field.UserIds; - public string[] UserNames => Field.GetUserNames(); - public UserPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] UserIds => Field.UserIds; + public string[] UserNames => Field.GetUserNames(); + public UserPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldViewModel.cs index e54ebdcae5a..8860c7aa5c1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/DisplayUserPickerFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class DisplayUserPickerFieldViewModel { - public class DisplayUserPickerFieldViewModel - { - public string[] UserIds => Field.UserIds; - public UserPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] UserIds => Field.UserIds; + public UserPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditBooleanFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditBooleanFieldViewModel.cs index 63bf35fbcde..3540c50ee55 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditBooleanFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditBooleanFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditBooleanFieldViewModel { - public class EditBooleanFieldViewModel - { - public bool Value { get; set; } - public BooleanField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public bool Value { get; set; } + public BooleanField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditContentPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditContentPickerFieldViewModel.cs index a6b6b921c3c..da3efdb5af8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditContentPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditContentPickerFieldViewModel.cs @@ -4,16 +4,15 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditContentPickerFieldViewModel { - public class EditContentPickerFieldViewModel - { - public string ContentItemIds { get; set; } - public ContentPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } + public string ContentItemIds { get; set; } + public ContentPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } - [BindNever] - public IList SelectedItems { get; set; } - } + [BindNever] + public IList SelectedItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateFieldViewModel.cs index 1cb843b1952..10330f199b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateFieldViewModel.cs @@ -3,13 +3,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditDateFieldViewModel { - public class EditDateFieldViewModel - { - public DateTime? Value { get; set; } - public DateField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public DateTime? Value { get; set; } + public DateField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateTimeFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateTimeFieldViewModel.cs index 65c2c552c59..1f3748bdfad 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateTimeFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditDateTimeFieldViewModel.cs @@ -3,13 +3,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditDateTimeFieldViewModel { - public class EditDateTimeFieldViewModel - { - public DateTime? LocalDateTime { get; set; } - public DateTimeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public DateTime? LocalDateTime { get; set; } + public DateTimeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditHtmlFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditHtmlFieldViewModel.cs index a2b9096d9af..23e4dfa2b75 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditHtmlFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditHtmlFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditHtmlFieldViewModel { - public class EditHtmlFieldViewModel - { - public string Html { get; set; } - public HtmlField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Html { get; set; } + public HtmlField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLinkFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLinkFieldViewModel.cs index 63660304624..509a8d473db 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLinkFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLinkFieldViewModel.cs @@ -2,15 +2,14 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditLinkFieldViewModel { - public class EditLinkFieldViewModel - { - public string Url { get; set; } - public string Text { get; set; } - public string Target { get; set; } - public LinkField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Url { get; set; } + public string Text { get; set; } + public string Target { get; set; } + public LinkField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLocalizationSetContentPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLocalizationSetContentPickerFieldViewModel.cs index 2de79ab686a..831f3218407 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLocalizationSetContentPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditLocalizationSetContentPickerFieldViewModel.cs @@ -4,16 +4,15 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditLocalizationSetContentPickerFieldViewModel { - public class EditLocalizationSetContentPickerFieldViewModel - { - public string LocalizationSets { get; set; } - public LocalizationSetContentPickerField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } + public string LocalizationSets { get; set; } + public LocalizationSetContentPickerField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } - [BindNever] - public IList SelectedItems { get; set; } - } + [BindNever] + public IList SelectedItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditMultiTextFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditMultiTextFieldViewModel.cs index a802588280a..37a9c3bb951 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditMultiTextFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditMultiTextFieldViewModel.cs @@ -3,19 +3,18 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditMultiTextFieldViewModel { - public class EditMultiTextFieldViewModel - { - public string[] Values { get; set; } = []; + public string[] Values { get; set; } = []; - [BindNever] - public MultiTextField Field { get; set; } + [BindNever] + public MultiTextField Field { get; set; } - [BindNever] - public ContentPart Part { get; set; } + [BindNever] + public ContentPart Part { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditNumericFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditNumericFieldViewModel.cs index af3d3a85869..87c55cfbc67 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditNumericFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditNumericFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditNumericFieldViewModel { - public class EditNumericFieldViewModel - { - public string Value { get; set; } - public NumericField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Value { get; set; } + public NumericField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTextFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTextFieldViewModel.cs index 4c6a346d870..5e303cc165b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTextFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTextFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditTextFieldViewModel { - public class EditTextFieldViewModel - { - public string Text { get; set; } - public TextField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Text { get; set; } + public TextField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTimeFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTimeFieldViewModel.cs index 8652ccc0752..3bc93f62aa0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTimeFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditTimeFieldViewModel.cs @@ -3,13 +3,12 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditTimeFieldViewModel { - public class EditTimeFieldViewModel - { - public TimeSpan? Value { get; set; } - public TimeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public TimeSpan? Value { get; set; } + public TimeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditUserPickerFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditUserPickerFieldViewModel.cs index 1a8cc9b1186..f38937f1059 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditUserPickerFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditUserPickerFieldViewModel.cs @@ -4,32 +4,31 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditUserPickerFieldViewModel { - public class EditUserPickerFieldViewModel - { - public string UserIds { get; set; } + public string UserIds { get; set; } - [BindNever] - public UserPickerField Field { get; set; } + [BindNever] + public UserPickerField Field { get; set; } - [BindNever] - public ContentPart Part { get; set; } + [BindNever] + public ContentPart Part { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } - [BindNever] - public ContentTypePartDefinition TypePartDefinition { get; set; } + [BindNever] + public ContentTypePartDefinition TypePartDefinition { get; set; } - [BindNever] - public IList SelectedUsers { get; set; } = []; - } + [BindNever] + public IList SelectedUsers { get; set; } = []; +} - public class VueMultiselectUserViewModel - { - public string Id { get; set; } - public string DisplayText { get; set; } - public bool IsEnabled { get; set; } - } +public class VueMultiselectUserViewModel +{ + public string Id { get; set; } + public string DisplayText { get; set; } + public bool IsEnabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditYoutubeFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditYoutubeFieldViewModel.cs index a9872228251..2deff7deb51 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditYoutubeFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/EditYoutubeFieldViewModel.cs @@ -3,16 +3,15 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class EditYoutubeFieldViewModel { - public class EditYoutubeFieldViewModel - { - [DataType(DataType.Url, ErrorMessage = "The field only accepts Urls")] - public string RawAddress { get; set; } + [DataType(DataType.Url, ErrorMessage = "The field only accepts Urls")] + public string RawAddress { get; set; } - public string EmbeddedAddress { get; set; } - public YoutubeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string EmbeddedAddress { get; set; } + public YoutubeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HeaderSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HeaderSettingsViewModel.cs index 8010860098e..984de604ac3 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HeaderSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HeaderSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class HeaderSettingsViewModel { - public class HeaderSettingsViewModel - { - public string Level { get; set; } - } + public string Level { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HtmlSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HtmlSettingsViewModel.cs index d57977a1121..40c6001cbe8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HtmlSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/HtmlSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class HtmlSettingsViewModel { - public class HtmlSettingsViewModel - { - public bool SanitizeHtml { get; set; } - public string Hint { get; set; } - } + public bool SanitizeHtml { get; set; } + public string Hint { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MonacoSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MonacoSettingsViewModel.cs index 1e4276cb7c9..7a94a8a3658 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MonacoSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MonacoSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class MonacoSettingsViewModel { - public class MonacoSettingsViewModel - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MultiTextFieldSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MultiTextFieldSettingsViewModel.cs index 7566cbb7d11..9e798426288 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MultiTextFieldSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/MultiTextFieldSettingsViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class MultiTextFieldSettingsViewModel { - public class MultiTextFieldSettingsViewModel - { - public string Hint { get; set; } - public bool Required { get; set; } - public string Options { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } + public string Options { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/PredefinedListSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/PredefinedListSettingsViewModel.cs index 892505e336b..251d6534a1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/PredefinedListSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/PredefinedListSettingsViewModel.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentFields.Settings; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class PredefinedListSettingsViewModel { - public class PredefinedListSettingsViewModel - { - public EditorOption Editor { get; set; } - public string Options { get; set; } - public string DefaultValue { get; set; } - } + public EditorOption Editor { get; set; } + public string Options { get; set; } + public string DefaultValue { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/TrumbowygSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/TrumbowygSettingsViewModel.cs index dcc3e6e1412..9f9f781a059 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/TrumbowygSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/TrumbowygSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class TrumbowygSettingsViewModel { - public class TrumbowygSettingsViewModel - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/VueMultiselectItemViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/VueMultiselectItemViewModel.cs index 0b87f027d56..6dbde7e4061 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/VueMultiselectItemViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/VueMultiselectItemViewModel.cs @@ -1,12 +1,11 @@ -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class VueMultiselectItemViewModel { - public class VueMultiselectItemViewModel - { - public string Id { get; set; } - public string DisplayText { get; set; } - public bool HasPublished { get; set; } - public bool IsViewable { get; set; } - public bool IsEditable { get; set; } - public bool IsClickable => IsEditable || IsViewable; - } + public string Id { get; set; } + public string DisplayText { get; set; } + public bool HasPublished { get; set; } + public bool IsViewable { get; set; } + public bool IsEditable { get; set; } + public bool IsClickable => IsEditable || IsViewable; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/YoutubeFieldDisplayViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/YoutubeFieldDisplayViewModel.cs index a0fe2e591a0..5f7099c8dd5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/YoutubeFieldDisplayViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentFields/ViewModels/YoutubeFieldDisplayViewModel.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentFields.ViewModels +namespace OrchardCore.ContentFields.ViewModels; + +public class YoutubeFieldDisplayViewModel { - public class YoutubeFieldDisplayViewModel - { - public string EmbeddedAddress => Field.EmbeddedAddress; - public string RawAddress => Field.RawAddress; - public YoutubeField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string EmbeddedAddress => Field.EmbeddedAddress; + public string RawAddress => Field.RawAddress; + public YoutubeField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/AdminMenu.cs index a56166c3f83..a7c059a6569 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/AdminMenu.cs @@ -4,59 +4,58 @@ using OrchardCore.ContentLocalization.Drivers; using OrchardCore.Navigation; -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _providersRouteValues = new() { - private static readonly RouteValueDictionary _providersRouteValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", ContentRequestCultureProviderSettingsDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", ContentRequestCultureProviderSettingsDriver.GroupId }, + }; - private static readonly RouteValueDictionary _pickerRouteValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", ContentCulturePickerSettingsDriver.GroupId }, - }; + private static readonly RouteValueDictionary _pickerRouteValues = new() + { + { "area", "OrchardCore.Settings" }, + { "groupId", ContentCulturePickerSettingsDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Localization"], localization => localization - .Add(S["Content Request Culture Provider"], S["Content Request Culture Provider"].PrefixPosition(), provider => provider - .AddClass("contentrequestcultureprovider") - .Id("contentrequestcultureprovider") - .Action("Index", "Admin", _providersRouteValues) - .Permission(Permissions.ManageContentCulturePicker) - .LocalNav() - ) - .Add(S["Content Culture Picker"], S["Content Culture Picker"].PrefixPosition(), picker => picker - .AddClass("contentculturepicker") - .Id("contentculturepicker") - .Action("Index", "Admin", _pickerRouteValues) - .Permission(Permissions.ManageContentCulturePicker) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Localization"], localization => localization + .Add(S["Content Request Culture Provider"], S["Content Request Culture Provider"].PrefixPosition(), provider => provider + .AddClass("contentrequestcultureprovider") + .Id("contentrequestcultureprovider") + .Action("Index", "Admin", _providersRouteValues) + .Permission(Permissions.ManageContentCulturePicker) + .LocalNav() + ) + .Add(S["Content Culture Picker"], S["Content Culture Picker"].PrefixPosition(), picker => picker + .AddClass("contentculturepicker") + .Id("contentculturepicker") + .Action("Index", "Admin", _pickerRouteValues) + .Permission(Permissions.ManageContentCulturePicker) + .LocalNav() ) ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ContentRequestCultureProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ContentRequestCultureProvider.cs index 6302191019f..b5e1882a0d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ContentRequestCultureProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ContentRequestCultureProvider.cs @@ -7,33 +7,32 @@ using OrchardCore.ContentLocalization.Services; using OrchardCore.Settings; -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +/// +/// RequestCultureProvider that automatically sets the Culture of a request from the LocalizationPart.Culture property. +/// +public class ContentRequestCultureProvider : RequestCultureProvider { - /// - /// RequestCultureProvider that automatically sets the Culture of a request from the LocalizationPart.Culture property. - /// - public class ContentRequestCultureProvider : RequestCultureProvider + public override async Task DetermineProviderCultureResult(HttpContext httpContext) { - public override async Task DetermineProviderCultureResult(HttpContext httpContext) - { - ArgumentNullException.ThrowIfNull(httpContext); + ArgumentNullException.ThrowIfNull(httpContext); - var culturePickerService = httpContext.RequestServices.GetService(); - var siteService = httpContext.RequestServices.GetService(); - var localization = await culturePickerService.GetLocalizationFromRouteAsync(httpContext.Request.Path); + var culturePickerService = httpContext.RequestServices.GetService(); + var siteService = httpContext.RequestServices.GetService(); + var localization = await culturePickerService.GetLocalizationFromRouteAsync(httpContext.Request.Path); - if (localization != null) + if (localization != null) + { + var settings = await siteService.GetSettingsAsync(); + if (settings.SetCookie) { - var settings = await siteService.GetSettingsAsync(); - if (settings.SetCookie) - { - culturePickerService.SetContentCulturePickerCookie(localization.Culture); - } - - return new ProviderCultureResult(localization.Culture); + culturePickerService.SetContentCulturePickerCookie(localization.Culture); } - return default; + return new ProviderCultureResult(localization.Culture); } + + return default; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/AdminController.cs index 1b7c1d5346f..313703277f9 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/AdminController.cs @@ -10,85 +10,84 @@ using OrchardCore.Contents; using OrchardCore.DisplayManagement.Notify; -namespace OrchardCore.ContentLocalization.Controllers +namespace OrchardCore.ContentLocalization.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IContentManager _contentManager; + private readonly IContentLocalizationManager _contentLocalizationManager; + private readonly INotifier _notifier; + private readonly IAuthorizationService _authorizationService; + protected readonly IHtmlLocalizer H; + + public AdminController( + IContentManager contentManager, + INotifier notifier, + IContentLocalizationManager localizationManager, + IHtmlLocalizer localizer, + IAuthorizationService authorizationService) + { + _contentManager = contentManager; + _notifier = notifier; + _authorizationService = authorizationService; + _contentLocalizationManager = localizationManager; + H = localizer; + } + + [HttpPost] + [Admin("ContentLocalization", "ContentLocalization.Localize")] + public async Task Localize(string contentItemId, string targetCulture, string returnUrl = null) { - private readonly IContentManager _contentManager; - private readonly IContentLocalizationManager _contentLocalizationManager; - private readonly INotifier _notifier; - private readonly IAuthorizationService _authorizationService; - protected readonly IHtmlLocalizer H; + // Invariant culture name is empty so a null value is bound. + targetCulture ??= string.Empty; - public AdminController( - IContentManager contentManager, - INotifier notifier, - IContentLocalizationManager localizationManager, - IHtmlLocalizer localizer, - IAuthorizationService authorizationService) + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + + if (contentItem == null) { - _contentManager = contentManager; - _notifier = notifier; - _authorizationService = authorizationService; - _contentLocalizationManager = localizationManager; - H = localizer; + return NotFound(); } - [HttpPost] - [Admin("ContentLocalization", "ContentLocalization.Localize")] - public async Task Localize(string contentItemId, string targetCulture, string returnUrl = null) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.LocalizeContent, contentItem)) { - // Invariant culture name is empty so a null value is bound. - targetCulture ??= string.Empty; - - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - - if (contentItem == null) - { - return NotFound(); - } - - if (!await _authorizationService.AuthorizeAsync(User, Permissions.LocalizeContent, contentItem)) - { - return Forbid(); - } + return Forbid(); + } - var checkContentItem = await _contentManager.NewAsync(contentItem.ContentType); + var checkContentItem = await _contentManager.NewAsync(contentItem.ContentType); - // Set the current user as the owner to check for ownership permissions on creation - checkContentItem.Owner = User.FindFirstValue(ClaimTypes.NameIdentifier); + // Set the current user as the owner to check for ownership permissions on creation + checkContentItem.Owner = User.FindFirstValue(ClaimTypes.NameIdentifier); - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, checkContentItem)) - { - return Forbid(); - } + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, checkContentItem)) + { + return Forbid(); + } - var part = contentItem.As(); + var part = contentItem.As(); - if (part == null) - { - return NotFound(); - } + if (part == null) + { + return NotFound(); + } - var alreadyLocalizedContent = await _contentLocalizationManager.GetContentItemAsync(part.LocalizationSet, targetCulture); + var alreadyLocalizedContent = await _contentLocalizationManager.GetContentItemAsync(part.LocalizationSet, targetCulture); - if (alreadyLocalizedContent != null) - { - await _notifier.WarningAsync(H["A localization already exists for '{0}'.", targetCulture]); - return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId, returnUrl }); - } + if (alreadyLocalizedContent != null) + { + await _notifier.WarningAsync(H["A localization already exists for '{0}'.", targetCulture]); + return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId, returnUrl }); + } - try - { - var newContent = await _contentLocalizationManager.LocalizeAsync(contentItem, targetCulture); - await _notifier.InformationAsync(H["Localized version of the content created successfully."]); - return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId = newContent.ContentItemId, returnUrl }); - } - catch (InvalidOperationException) - { - await _notifier.WarningAsync(H["Could not create localized version of the content item."]); - return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId = contentItem.ContentItemId, returnUrl }); - } + try + { + var newContent = await _contentLocalizationManager.LocalizeAsync(contentItem, targetCulture); + await _notifier.InformationAsync(H["Localized version of the content created successfully."]); + return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId = newContent.ContentItemId, returnUrl }); + } + catch (InvalidOperationException) + { + await _notifier.WarningAsync(H["Could not create localized version of the content item."]); + return RedirectToAction("Edit", "Admin", new { area = "OrchardCore.Contents", contentItemId = contentItem.ContentItemId, returnUrl }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/ContentCulturePickerController.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/ContentCulturePickerController.cs index bf8177fa01e..42d1e2ec54c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/ContentCulturePickerController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Controllers/ContentCulturePickerController.cs @@ -11,76 +11,75 @@ using OrchardCore.Modules; using OrchardCore.Settings; -namespace OrchardCore.ContentLocalization.Controllers +namespace OrchardCore.ContentLocalization.Controllers; + +[Feature("OrchardCore.ContentLocalization.ContentCulturePicker")] +public class ContentCulturePickerController : Controller { - [Feature("OrchardCore.ContentLocalization.ContentCulturePicker")] - public class ContentCulturePickerController : Controller + private readonly ISiteService _siteService; + private readonly ILocalizationService _locationService; + private readonly IContentCulturePickerService _culturePickerService; + + public ContentCulturePickerController( + ISiteService siteService, + ILocalizationService locationService, + IContentCulturePickerService culturePickerService) + { + _siteService = siteService; + _locationService = locationService; + _culturePickerService = culturePickerService; + } + + [HttpGet] + public async Task RedirectToLocalizedContent(string targetCulture, PathString contentItemUrl, string queryStringValue) { - private readonly ISiteService _siteService; - private readonly ILocalizationService _locationService; - private readonly IContentCulturePickerService _culturePickerService; + targetCulture ??= CultureInfo.InvariantCulture.Name; - public ContentCulturePickerController( - ISiteService siteService, - ILocalizationService locationService, - IContentCulturePickerService culturePickerService) + if (!contentItemUrl.HasValue) { - _siteService = siteService; - _locationService = locationService; - _culturePickerService = culturePickerService; + contentItemUrl = "/"; } - [HttpGet] - public async Task RedirectToLocalizedContent(string targetCulture, PathString contentItemUrl, string queryStringValue) - { - targetCulture ??= CultureInfo.InvariantCulture.Name; + var queryString = new QueryString(queryStringValue); + var url = HttpContext.Request.PathBase + contentItemUrl + queryString; - if (!contentItemUrl.HasValue) + var supportedCultures = await _locationService.GetSupportedCulturesAsync(); + + if (supportedCultures.Any(t => t == targetCulture)) + { + var settings = await _siteService.GetSettingsAsync(); + if (settings.SetCookie) { - contentItemUrl = "/"; + _culturePickerService.SetContentCulturePickerCookie(targetCulture); } - var queryString = new QueryString(queryStringValue); - var url = HttpContext.Request.PathBase + contentItemUrl + queryString; - - var supportedCultures = await _locationService.GetSupportedCulturesAsync(); - - if (supportedCultures.Any(t => t == targetCulture)) + // Redirect the user to the Content with the same localizationSet as the ContentItem of the current url + var localizations = await _culturePickerService.GetLocalizationsFromRouteAsync(contentItemUrl); + if (!TryGetLocalizedContentUrl(localizations) && settings.RedirectToHomepage) { - var settings = await _siteService.GetSettingsAsync(); - if (settings.SetCookie) - { - _culturePickerService.SetContentCulturePickerCookie(targetCulture); - } - - // Redirect the user to the Content with the same localizationSet as the ContentItem of the current url - var localizations = await _culturePickerService.GetLocalizationsFromRouteAsync(contentItemUrl); - if (!TryGetLocalizedContentUrl(localizations) && settings.RedirectToHomepage) - { - // Try to get the Homepage for the current culture - var homeLocalizations = await _culturePickerService.GetLocalizationsFromRouteAsync("/"); - TryGetLocalizedContentUrl(homeLocalizations); - } + // Try to get the Homepage for the current culture + var homeLocalizations = await _culturePickerService.GetLocalizationsFromRouteAsync("/"); + TryGetLocalizedContentUrl(homeLocalizations); } + } - bool TryGetLocalizedContentUrl(IEnumerable localizationEntries) + bool TryGetLocalizedContentUrl(IEnumerable localizationEntries) + { + if (localizationEntries.Any()) { - if (localizationEntries.Any()) - { - var localization = localizationEntries.SingleOrDefault(e => string.Equals(e.Culture, targetCulture, StringComparison.OrdinalIgnoreCase)); + var localization = localizationEntries.SingleOrDefault(e => string.Equals(e.Culture, targetCulture, StringComparison.OrdinalIgnoreCase)); - if (localization != null) - { - url = Url.Action("Display", "Item", new { Area = "OrchardCore.Contents", contentItemId = localization.ContentItemId }) + queryString; + if (localization != null) + { + url = Url.Action("Display", "Item", new { Area = "OrchardCore.Contents", contentItemId = localization.ContentItemId }) + queryString; - return true; - } + return true; } - - return false; } - return this.LocalRedirect(url, true); + return false; } + + return this.LocalRedirect(url, true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/CulturePickerOptions.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/CulturePickerOptions.cs index 5dd607efbac..37019177119 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/CulturePickerOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/CulturePickerOptions.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +public class CulturePickerOptions { - public class CulturePickerOptions - { - public int CookieLifeTime { get; set; } = 14; - } + public int CookieLifeTime { get; set; } = 14; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/DefaultContentLocalizationManager.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/DefaultContentLocalizationManager.cs index 85c24505a43..2edfdb20a09 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/DefaultContentLocalizationManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/DefaultContentLocalizationManager.cs @@ -14,175 +14,174 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +public class DefaultContentLocalizationManager : IContentLocalizationManager { - public class DefaultContentLocalizationManager : IContentLocalizationManager + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly Microsoft.AspNetCore.Http.IHttpContextAccessor _httpContextAccessor; + private readonly ILocalizationService _localizationService; + private readonly ILogger _logger; + private readonly Entities.IIdGenerator _iidGenerator; + + public IEnumerable Handlers { get; private set; } + public IEnumerable ReversedHandlers { get; private set; } + + public DefaultContentLocalizationManager( + IContentManager contentManager, + ISession session, + Microsoft.AspNetCore.Http.IHttpContextAccessor httpContentAccessor, + ILocalizationService localizationService, + ILogger logger, + IEnumerable handlers, + Entities.IIdGenerator iidGenerator) { - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly Microsoft.AspNetCore.Http.IHttpContextAccessor _httpContextAccessor; - private readonly ILocalizationService _localizationService; - private readonly ILogger _logger; - private readonly Entities.IIdGenerator _iidGenerator; - - public IEnumerable Handlers { get; private set; } - public IEnumerable ReversedHandlers { get; private set; } - - public DefaultContentLocalizationManager( - IContentManager contentManager, - ISession session, - Microsoft.AspNetCore.Http.IHttpContextAccessor httpContentAccessor, - ILocalizationService localizationService, - ILogger logger, - IEnumerable handlers, - Entities.IIdGenerator iidGenerator) - { - _contentManager = contentManager; - _session = session; - _httpContextAccessor = httpContentAccessor; - _localizationService = localizationService; - Handlers = handlers; - _iidGenerator = iidGenerator; - ReversedHandlers = handlers.Reverse().ToArray(); - _logger = logger; - } + _contentManager = contentManager; + _session = session; + _httpContextAccessor = httpContentAccessor; + _localizationService = localizationService; + Handlers = handlers; + _iidGenerator = iidGenerator; + ReversedHandlers = handlers.Reverse().ToArray(); + _logger = logger; + } - public Task GetContentItemAsync(string localizationSet, string culture) - { - var invariantCulture = culture.ToLowerInvariant(); - return _session.Query(i => - (i.Published || i.Latest) && - i.LocalizationSet == localizationSet && - i.Culture == invariantCulture) - .FirstOrDefaultAsync(); - } + public Task GetContentItemAsync(string localizationSet, string culture) + { + var invariantCulture = culture.ToLowerInvariant(); + return _session.Query(i => + (i.Published || i.Latest) && + i.LocalizationSet == localizationSet && + i.Culture == invariantCulture) + .FirstOrDefaultAsync(); + } + + public Task> GetItemsForSetAsync(string localizationSet) + { + return _session.Query(i => (i.Published || i.Latest) && i.LocalizationSet == localizationSet).ListAsync(); + } - public Task> GetItemsForSetAsync(string localizationSet) + public Task> GetItemsForSetsAsync(IEnumerable localizationSets, string culture) + { + var invariantCulture = culture.ToLowerInvariant(); + return _session.Query(i => (i.Published || i.Latest) && i.LocalizationSet.IsIn(localizationSets) && i.Culture == invariantCulture).ListAsync(); + } + + public async Task LocalizeAsync(ContentItem content, string targetCulture) + { + var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); + if (!supportedCultures.Any(c => string.Equals(c, targetCulture, StringComparison.OrdinalIgnoreCase))) { - return _session.Query(i => (i.Published || i.Latest) && i.LocalizationSet == localizationSet).ListAsync(); + throw new InvalidOperationException("Cannot localize an unsupported culture"); } - public Task> GetItemsForSetsAsync(IEnumerable localizationSets, string culture) + var localizationPart = content.As(); + if (string.IsNullOrEmpty(localizationPart.LocalizationSet)) { - var invariantCulture = culture.ToLowerInvariant(); - return _session.Query(i => (i.Published || i.Latest) && i.LocalizationSet.IsIn(localizationSets) && i.Culture == invariantCulture).ListAsync(); + // If the source content item is not yet localized, define its defaults. + localizationPart.LocalizationSet = _iidGenerator.GenerateUniqueId(); + localizationPart.Culture = await _localizationService.GetDefaultCultureAsync(); + await _session.SaveAsync(content); } - - public async Task LocalizeAsync(ContentItem content, string targetCulture) + else { - var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); - if (!supportedCultures.Any(c => string.Equals(c, targetCulture, StringComparison.OrdinalIgnoreCase))) - { - throw new InvalidOperationException("Cannot localize an unsupported culture"); - } + var existingContent = await GetContentItemAsync(localizationPart.LocalizationSet, targetCulture); - var localizationPart = content.As(); - if (string.IsNullOrEmpty(localizationPart.LocalizationSet)) + if (existingContent != null) { - // If the source content item is not yet localized, define its defaults. - localizationPart.LocalizationSet = _iidGenerator.GenerateUniqueId(); - localizationPart.Culture = await _localizationService.GetDefaultCultureAsync(); - await _session.SaveAsync(content); + // Already localized. + return existingContent; } - else - { - var existingContent = await GetContentItemAsync(localizationPart.LocalizationSet, targetCulture); + } - if (existingContent != null) - { - // Already localized. - return existingContent; - } - } + // Cloning the content item. + var cloned = await _contentManager.CloneAsync(content); + var clonedPart = cloned.As(); + clonedPart.Culture = targetCulture; + clonedPart.LocalizationSet = localizationPart.LocalizationSet; + clonedPart.Apply(); + + var context = new LocalizationContentContext(cloned, content, localizationPart.LocalizationSet, targetCulture); - // Cloning the content item. - var cloned = await _contentManager.CloneAsync(content); - var clonedPart = cloned.As(); - clonedPart.Culture = targetCulture; - clonedPart.LocalizationSet = localizationPart.LocalizationSet; - clonedPart.Apply(); + await Handlers.InvokeAsync((handler, context) => handler.LocalizingAsync(context), context, _logger); + await ReversedHandlers.InvokeAsync((handler, context) => handler.LocalizedAsync(context), context, _logger); - var context = new LocalizationContentContext(cloned, content, localizationPart.LocalizationSet, targetCulture); + return cloned; + } - await Handlers.InvokeAsync((handler, context) => handler.LocalizingAsync(context), context, _logger); - await ReversedHandlers.InvokeAsync((handler, context) => handler.LocalizedAsync(context), context, _logger); + public async Task> DeduplicateContentItemsAsync(IEnumerable contentItems) + { + var contentItemIds = contentItems.Select(c => c.ContentItemId); + var indexValues = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId.IsIn(contentItemIds)).ListAsync(); - return cloned; - } + var currentCulture = _httpContextAccessor.HttpContext.Features.Get().RequestCulture.Culture.Name.ToLowerInvariant(); + var defaultCulture = (await _localizationService.GetDefaultCultureAsync()).ToLowerInvariant(); + var cleanedIndexValues = GetSingleContentItemIdPerSet(indexValues, currentCulture, defaultCulture); - public async Task> DeduplicateContentItemsAsync(IEnumerable contentItems) + var dictionary = new Dictionary(); + foreach (var val in cleanedIndexValues) { - var contentItemIds = contentItems.Select(c => c.ContentItemId); - var indexValues = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId.IsIn(contentItemIds)).ListAsync(); + dictionary.Add(val.LocalizationSet, contentItems.SingleOrDefault(ci => ci.ContentItemId == val.ContentItemId)); + } - var currentCulture = _httpContextAccessor.HttpContext.Features.Get().RequestCulture.Culture.Name.ToLowerInvariant(); - var defaultCulture = (await _localizationService.GetDefaultCultureAsync()).ToLowerInvariant(); - var cleanedIndexValues = GetSingleContentItemIdPerSet(indexValues, currentCulture, defaultCulture); + return dictionary; + } - var dictionary = new Dictionary(); - foreach (var val in cleanedIndexValues) + public async Task> GetFirstItemIdForSetsAsync(IEnumerable localizationSets) + { + var indexValues = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet.IsIn(localizationSets)).ListAsync(); + + var currentCulture = _httpContextAccessor.HttpContext.Features.Get().RequestCulture.Culture.Name.ToLowerInvariant(); + var defaultCulture = (await _localizationService.GetDefaultCultureAsync()).ToLowerInvariant(); + var dictionary = new Dictionary(); + var cleanedIndexValues = GetSingleContentItemIdPerSet(indexValues, currentCulture, defaultCulture); + + // This loop keeps the original ordering of localizationSets for the LocalizationSetContentPicker. + foreach (var set in localizationSets) + { + var idxValue = cleanedIndexValues.FirstOrDefault(x => x.LocalizationSet == set); + if (idxValue == null) { - dictionary.Add(val.LocalizationSet, contentItems.SingleOrDefault(ci => ci.ContentItemId == val.ContentItemId)); + continue; } - return dictionary; + dictionary.Add(idxValue.LocalizationSet, idxValue.ContentItemId); } - public async Task> GetFirstItemIdForSetsAsync(IEnumerable localizationSets) - { - var indexValues = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet.IsIn(localizationSets)).ListAsync(); - - var currentCulture = _httpContextAccessor.HttpContext.Features.Get().RequestCulture.Culture.Name.ToLowerInvariant(); - var defaultCulture = (await _localizationService.GetDefaultCultureAsync()).ToLowerInvariant(); - var dictionary = new Dictionary(); - var cleanedIndexValues = GetSingleContentItemIdPerSet(indexValues, currentCulture, defaultCulture); + return dictionary; + } - // This loop keeps the original ordering of localizationSets for the LocalizationSetContentPicker. - foreach (var set in localizationSets) + /// + /// ContentItemId chosen with the following rules: + /// ContentItemId of the current culture for the set + /// OR ContentItemId of the default culture for the set + /// OR First ContentItemId found in the set + /// OR null if nothing found. + /// + /// List of ContentItemId. + private static List GetSingleContentItemIdPerSet(IEnumerable indexValues, string currentCulture, string defaultCulture) + { + return indexValues.GroupBy(l => l.LocalizationSet).Select(set => + { + var currentCultureContentItem = set.FirstOrDefault(f => f.Culture == currentCulture); + if (currentCultureContentItem is not null) { - var idxValue = cleanedIndexValues.FirstOrDefault(x => x.LocalizationSet == set); - if (idxValue == null) - { - continue; - } - - dictionary.Add(idxValue.LocalizationSet, idxValue.ContentItemId); + return currentCultureContentItem; } - return dictionary; - } + var defaultCultureContentItem = set.FirstOrDefault(f => f.Culture == defaultCulture); + if (defaultCultureContentItem is not null) + { + return defaultCultureContentItem; + } - /// - /// ContentItemId chosen with the following rules: - /// ContentItemId of the current culture for the set - /// OR ContentItemId of the default culture for the set - /// OR First ContentItemId found in the set - /// OR null if nothing found. - /// - /// List of ContentItemId. - private static List GetSingleContentItemIdPerSet(IEnumerable indexValues, string currentCulture, string defaultCulture) - { - return indexValues.GroupBy(l => l.LocalizationSet).Select(set => + if (set.Any()) { - var currentCultureContentItem = set.FirstOrDefault(f => f.Culture == currentCulture); - if (currentCultureContentItem is not null) - { - return currentCultureContentItem; - } - - var defaultCultureContentItem = set.FirstOrDefault(f => f.Culture == defaultCulture); - if (defaultCultureContentItem is not null) - { - return defaultCultureContentItem; - } - - if (set.Any()) - { - return set.FirstOrDefault(); - } - - return null; - }).OfType().ToList(); - } + return set.FirstOrDefault(); + } + + return null; + }).OfType().ToList(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentCulturePickerSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentCulturePickerSettingsDriver.cs index 98ee5aeeebe..21110adf925 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentCulturePickerSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentCulturePickerSettingsDriver.cs @@ -7,55 +7,54 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.ContentLocalization.Drivers +namespace OrchardCore.ContentLocalization.Drivers; + +public sealed class ContentCulturePickerSettingsDriver : SiteDisplayDriver { - public sealed class ContentCulturePickerSettingsDriver : SiteDisplayDriver - { - public const string GroupId = "ContentCulturePicker"; + public const string GroupId = "ContentCulturePicker"; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; - public ContentCulturePickerSettingsDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + public ContentCulturePickerSettingsDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) + { + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } + + protected override string SettingsGroupId + => GroupId; - protected override string SettingsGroupId - => GroupId; + public override async Task EditAsync(ISite site, ContentCulturePickerSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task EditAsync(ISite site, ContentCulturePickerSettings settings, BuildEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) - { - return null; - } - - return Initialize("ContentCulturePickerSettings_Edit", model => - { - model.SetCookie = settings.SetCookie; - model.RedirectToHomepage = settings.RedirectToHomepage; - }).Location("Content:5") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, ContentCulturePickerSettings section, UpdateEditorContext context) + return Initialize("ContentCulturePickerSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) - { - return null; - } + model.SetCookie = settings.SetCookie; + model.RedirectToHomepage = settings.RedirectToHomepage; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - await context.Updater.TryUpdateModelAsync(section, Prefix); + public override async Task UpdateAsync(ISite site, ContentCulturePickerSettings section, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - return await EditAsync(site, section, context); + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) + { + return null; } + + await context.Updater.TryUpdateModelAsync(section, Prefix); + + return await EditAsync(site, section, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentRequestCultureProviderSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentRequestCultureProviderSettingsDriver.cs index 7cdb2830c0a..b0579124b0e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentRequestCultureProviderSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/ContentRequestCultureProviderSettingsDriver.cs @@ -7,54 +7,53 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.ContentLocalization.Drivers +namespace OrchardCore.ContentLocalization.Drivers; + +public sealed class ContentRequestCultureProviderSettingsDriver : SiteDisplayDriver { - public sealed class ContentRequestCultureProviderSettingsDriver : SiteDisplayDriver - { - public const string GroupId = "ContentRequestCultureProvider"; + public const string GroupId = "ContentRequestCultureProvider"; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; - public ContentRequestCultureProviderSettingsDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + public ContentRequestCultureProviderSettingsDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) + { + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } + + protected override string SettingsGroupId + => GroupId; - protected override string SettingsGroupId - => GroupId; + public override async Task EditAsync(ISite site, ContentRequestCultureProviderSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task EditAsync(ISite site, ContentRequestCultureProviderSettings settings, BuildEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) - { - return null; - } - - return Initialize("ContentRequestCultureProviderSettings_Edit", model => - { - model.SetCookie = settings.SetCookie; - }).Location("Content:5") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, ContentRequestCultureProviderSettings settings, UpdateEditorContext context) + return Initialize("ContentRequestCultureProviderSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) - { - return null; - } + model.SetCookie = settings.SetCookie; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - await context.Updater.TryUpdateModelAsync(settings, Prefix); + public override async Task UpdateAsync(ISite site, ContentRequestCultureProviderSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - return await EditAsync(site, settings, context); + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageContentCulturePicker)) + { + return null; } + + await context.Updater.TryUpdateModelAsync(settings, Prefix); + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationContentsAdminListDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationContentsAdminListDisplayDriver.cs index 06d154adf8d..10bb4b556e6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationContentsAdminListDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationContentsAdminListDisplayDriver.cs @@ -9,69 +9,68 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Localization; -namespace OrchardCore.ContentLocalization.Drivers +namespace OrchardCore.ContentLocalization.Drivers; + +public sealed class LocalizationContentsAdminListDisplayDriver : DisplayDriver { - public sealed class LocalizationContentsAdminListDisplayDriver : DisplayDriver - { - private readonly ILocalizationService _localizationService; + private readonly ILocalizationService _localizationService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public LocalizationContentsAdminListDisplayDriver( - ILocalizationService localizationService, - IStringLocalizer stringLocalizer) - { - _localizationService = localizationService; - S = stringLocalizer; - } + public LocalizationContentsAdminListDisplayDriver( + ILocalizationService localizationService, + IStringLocalizer stringLocalizer) + { + _localizationService = localizationService; + S = stringLocalizer; + } - protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) - { - Prefix = "Localization"; - } + protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) + { + Prefix = "Localization"; + } - public override IDisplayResult Display(ContentOptionsViewModel model, BuildDisplayContext context) - => View("ContentsAdminFilters_Thumbnail__Culture", model).Location("Thumbnail", "Content:20.1"); + public override IDisplayResult Display(ContentOptionsViewModel model, BuildDisplayContext context) + => View("ContentsAdminFilters_Thumbnail__Culture", model).Location("Thumbnail", "Content:20.1"); - public override IDisplayResult Edit(ContentOptionsViewModel model, BuildEditorContext context) + public override IDisplayResult Edit(ContentOptionsViewModel model, BuildEditorContext context) + { + return Initialize("ContentsAdminList__LocalizationPartFilter", async m => { - return Initialize("ContentsAdminList__LocalizationPartFilter", async m => + model.FilterResult.MapTo(m); + var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); + var cultures = new List { - model.FilterResult.MapTo(m); - var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); - var cultures = new List + new() { - new() - { - Text = S["All cultures"], - Value = string.Empty, - Selected = string.IsNullOrEmpty(m.SelectedCulture) - } - }; - cultures.AddRange(supportedCultures.Select(culture => new SelectListItem() - { - Text = culture, - Value = culture, - Selected = culture == m.SelectedCulture, - })); + Text = S["All cultures"], + Value = string.Empty, + Selected = string.IsNullOrEmpty(m.SelectedCulture) + } + }; + cultures.AddRange(supportedCultures.Select(culture => new SelectListItem() + { + Text = culture, + Value = culture, + Selected = culture == m.SelectedCulture, + })); - m.Cultures = cultures; - }).Location("Actions:20"); - } + m.Cultures = cultures; + }).Location("Actions:20"); + } - public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) - { - var viewModel = new LocalizationContentsAdminFilterViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, "Localization"); + public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + { + var viewModel = new LocalizationContentsAdminFilterViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, "Localization"); - if (viewModel.ShowLocalizedContentTypes) - { - model.RouteValues.TryAdd("Localization.ShowLocalizedContentTypes", viewModel.ShowLocalizedContentTypes); - } + if (viewModel.ShowLocalizedContentTypes) + { + model.RouteValues.TryAdd("Localization.ShowLocalizedContentTypes", viewModel.ShowLocalizedContentTypes); + } - model.FilterResult.MapFrom(viewModel); + model.FilterResult.MapFrom(viewModel); - return Edit(model, context); - } + return Edit(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationPartDisplayDriver.cs index 95aa4188f24..0a8f8b872fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Drivers/LocalizationPartDisplayDriver.cs @@ -11,93 +11,92 @@ using OrchardCore.Entities; using OrchardCore.Localization; -namespace OrchardCore.ContentLocalization.Drivers +namespace OrchardCore.ContentLocalization.Drivers; + +public sealed class LocalizationPartDisplayDriver : ContentPartDisplayDriver { - public sealed class LocalizationPartDisplayDriver : ContentPartDisplayDriver - { - private readonly IContentLocalizationManager _contentLocalizationManager; - private readonly IIdGenerator _idGenerator; - private readonly ILocalizationService _localizationService; + private readonly IContentLocalizationManager _contentLocalizationManager; + private readonly IIdGenerator _idGenerator; + private readonly ILocalizationService _localizationService; - public LocalizationPartDisplayDriver( - IContentLocalizationManager contentLocalizationManager, - IIdGenerator idGenerator, - ILocalizationService localizationService - ) - { - _contentLocalizationManager = contentLocalizationManager; - _idGenerator = idGenerator; - _localizationService = localizationService; - } + public LocalizationPartDisplayDriver( + IContentLocalizationManager contentLocalizationManager, + IIdGenerator idGenerator, + ILocalizationService localizationService + ) + { + _contentLocalizationManager = contentLocalizationManager; + _idGenerator = idGenerator; + _localizationService = localizationService; + } - public override IDisplayResult Display(LocalizationPart part, BuildPartDisplayContext context) - { - return Combine( - Initialize("LocalizationPart_SummaryAdmin", model => BuildViewModelAsync(model, part)).Location("SummaryAdmin", "Tags:11"), - Initialize("LocalizationPart_SummaryAdminLinks", model => BuildViewModelAsync(model, part)).Location("SummaryAdmin", "Actions:5") - ); - } + public override IDisplayResult Display(LocalizationPart part, BuildPartDisplayContext context) + { + return Combine( + Initialize("LocalizationPart_SummaryAdmin", model => BuildViewModelAsync(model, part)).Location("SummaryAdmin", "Tags:11"), + Initialize("LocalizationPart_SummaryAdminLinks", model => BuildViewModelAsync(model, part)).Location("SummaryAdmin", "Actions:5") + ); + } - public override IDisplayResult Edit(LocalizationPart localizationPart, BuildPartEditorContext context) - { - return Initialize(GetEditorShapeType(context), m => BuildViewModelAsync(m, localizationPart)); - } + public override IDisplayResult Edit(LocalizationPart localizationPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), m => BuildViewModelAsync(m, localizationPart)); + } - public override async Task UpdateAsync(LocalizationPart model, UpdatePartEditorContext context) - { - var viewModel = new LocalizationPartViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Culture); + public override async Task UpdateAsync(LocalizationPart model, UpdatePartEditorContext context) + { + var viewModel = new LocalizationPartViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Culture); - // Invariant culture name is empty so a null value is bound. - model.Culture = viewModel.Culture ?? string.Empty; + // Invariant culture name is empty so a null value is bound. + model.Culture = viewModel.Culture ?? string.Empty; - // Need to do this here to support displaying the message to save before localizing when the item has not been saved yet. - if (string.IsNullOrEmpty(model.LocalizationSet)) - { - model.LocalizationSet = _idGenerator.GenerateUniqueId(); - } - return Edit(model, context); + // Need to do this here to support displaying the message to save before localizing when the item has not been saved yet. + if (string.IsNullOrEmpty(model.LocalizationSet)) + { + model.LocalizationSet = _idGenerator.GenerateUniqueId(); } + return Edit(model, context); + } - public async ValueTask BuildViewModelAsync(LocalizationPartViewModel model, LocalizationPart localizationPart) - { - var alreadyTranslated = await _contentLocalizationManager.GetItemsForSetAsync(localizationPart.LocalizationSet); + public async ValueTask BuildViewModelAsync(LocalizationPartViewModel model, LocalizationPart localizationPart) + { + var alreadyTranslated = await _contentLocalizationManager.GetItemsForSetAsync(localizationPart.LocalizationSet); - model.Culture = localizationPart.Culture; - model.LocalizationSet = localizationPart.LocalizationSet; - model.LocalizationPart = localizationPart; + model.Culture = localizationPart.Culture; + model.LocalizationSet = localizationPart.LocalizationSet; + model.LocalizationPart = localizationPart; - // Invariant culture name is empty so we only do a null check. - model.Culture ??= await _localizationService.GetDefaultCultureAsync(); + // Invariant culture name is empty so we only do a null check. + model.Culture ??= await _localizationService.GetDefaultCultureAsync(); - var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); - var currentCultures = supportedCultures.Where(c => c != model.Culture).Select(culture => + var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); + var currentCultures = supportedCultures.Where(c => c != model.Culture).Select(culture => + { + return new LocalizationLinksViewModel() { - return new LocalizationLinksViewModel() - { - IsDeleted = false, - Culture = CultureInfo.GetCultureInfo(culture), - ContentItemId = alreadyTranslated.FirstOrDefault(c => c.As()?.Culture == culture)?.ContentItemId, - }; - }).ToList(); + IsDeleted = false, + Culture = CultureInfo.GetCultureInfo(culture), + ContentItemId = alreadyTranslated.FirstOrDefault(c => c.As()?.Culture == culture)?.ContentItemId, + }; + }).ToList(); - // Content items that have been translated but the culture was removed from the settings page - var deletedCultureTranslations = alreadyTranslated.Where(c => c.As()?.Culture != model.Culture).Select(ci => + // Content items that have been translated but the culture was removed from the settings page + var deletedCultureTranslations = alreadyTranslated.Where(c => c.As()?.Culture != model.Culture).Select(ci => + { + var culture = ci.As()?.Culture; + if (currentCultures.Any(c => c.ContentItemId == ci.ContentItemId) || culture == null) { - var culture = ci.As()?.Culture; - if (currentCultures.Any(c => c.ContentItemId == ci.ContentItemId) || culture == null) - { - return null; - } - return new LocalizationLinksViewModel() - { - IsDeleted = true, - Culture = CultureInfo.GetCultureInfo(culture), - ContentItemId = ci?.ContentItemId - }; - }).OfType().ToList(); + return null; + } + return new LocalizationLinksViewModel() + { + IsDeleted = true, + Culture = CultureInfo.GetCultureInfo(culture), + ContentItemId = ci?.ContentItemId + }; + }).OfType().ToList(); - model.ContentItemCultures = currentCultures.Concat(deletedCultureTranslations).ToList(); - } + model.ContentItemCultures = currentCultures.Concat(deletedCultureTranslations).ToList(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs index d17d020fb91..11597400908 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationInputObjectType.cs @@ -3,17 +3,16 @@ using OrchardCore.Apis.GraphQL.Queries; using OrchardCore.ContentLocalization.Models; -namespace OrchardCore.ContentLocalization.GraphQL +namespace OrchardCore.ContentLocalization.GraphQL; + +public class LocalizationInputObjectType : WhereInputObjectGraphType { - public class LocalizationInputObjectType : WhereInputObjectGraphType + public LocalizationInputObjectType(IStringLocalizer S) { - public LocalizationInputObjectType(IStringLocalizer S) - { - Name = "LocalizationInputObjectType"; - Description = S["the localization part of the content item"]; + Name = "LocalizationInputObjectType"; + Description = S["the localization part of the content item"]; - AddScalarFilterFields("culture", S["the culture of the content item to filter"]); - Field(x => x.LocalizationSet, nullable: true).Description(S["the localization set of the content item to filter"]); - } + AddScalarFilterFields("culture", S["the culture of the content item to filter"]); + Field(x => x.LocalizationSet, nullable: true).Description(S["the localization set of the content item to filter"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationPartIndexAliasProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationPartIndexAliasProvider.cs index 6e904e89fa4..98beedc1267 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationPartIndexAliasProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationPartIndexAliasProvider.cs @@ -3,23 +3,22 @@ using OrchardCore.ContentLocalization.Records; using OrchardCore.ContentManagement.GraphQL.Queries; -namespace OrchardCore.ContentLocalization.GraphQL -{ - public class LocalizationPartIndexAliasProvider : IIndexAliasProvider - { - private static readonly IndexAlias[] _aliases = - [ - new IndexAlias - { - Alias = "localizationPart", - Index = nameof(LocalizedContentItemIndex), - IndexType = typeof(LocalizedContentItemIndex) - } - ]; +namespace OrchardCore.ContentLocalization.GraphQL; - public ValueTask> GetAliasesAsync() +public class LocalizationPartIndexAliasProvider : IIndexAliasProvider +{ + private static readonly IndexAlias[] _aliases = + [ + new IndexAlias { - return ValueTask.FromResult>(_aliases); + Alias = "localizationPart", + Index = nameof(LocalizedContentItemIndex), + IndexType = typeof(LocalizedContentItemIndex) } + ]; + + public ValueTask> GetAliasesAsync() + { + return ValueTask.FromResult>(_aliases); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationQueryObjectType.cs index d152b49227e..ba457799c86 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/LocalizationQueryObjectType.cs @@ -9,37 +9,36 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.GraphQL.Queries.Types; -namespace OrchardCore.ContentLocalization.GraphQL +namespace OrchardCore.ContentLocalization.GraphQL; + +public class LocalizationQueryObjectType : ObjectGraphType { - public class LocalizationQueryObjectType : ObjectGraphType + public LocalizationQueryObjectType(IStringLocalizer S) { - public LocalizationQueryObjectType(IStringLocalizer S) - { - Name = "LocalizationPart"; - Description = S["Localization cultures for your content item."]; + Name = "LocalizationPart"; + Description = S["Localization cultures for your content item."]; - Field(x => x.Culture).Description(S["The culture for your content item."]); - Field(x => x.LocalizationSet).Description(S["The localization set for your content item."]); + Field(x => x.Culture).Description(S["The culture for your content item."]); + Field(x => x.LocalizationSet).Description(S["The localization set for your content item."]); - Field, IEnumerable>("Localizations") - .Description(S["The localizations of the content item."]) - .Argument("culture", "the culture of the content item") - .ResolveLockedAsync(GetContentItemsByLocalizationSetAsync); - } - - private static async ValueTask> GetContentItemsByLocalizationSetAsync(IResolveFieldContext context) - { - var culture = context.GetArgument("culture"); - var contentLocalizationManager = context.RequestServices.GetService(); + Field, IEnumerable>("Localizations") + .Description(S["The localizations of the content item."]) + .Argument("culture", "the culture of the content item") + .ResolveLockedAsync(GetContentItemsByLocalizationSetAsync); + } - if (culture != null) - { - var contentItem = await contentLocalizationManager.GetContentItemAsync(context.Source.LocalizationSet, culture); + private static async ValueTask> GetContentItemsByLocalizationSetAsync(IResolveFieldContext context) + { + var culture = context.GetArgument("culture"); + var contentLocalizationManager = context.RequestServices.GetService(); - return contentItem != null ? new[] { contentItem } : []; - } + if (culture != null) + { + var contentItem = await contentLocalizationManager.GetContentItemAsync(context.Source.LocalizationSet, culture); - return await contentLocalizationManager.GetItemsForSetAsync(context.Source.LocalizationSet); + return contentItem != null ? new[] { contentItem } : []; } + + return await contentLocalizationManager.GetItemsForSetAsync(context.Source.LocalizationSet); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/Startup.cs index 18371795815..35dd01bf283 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/GraphQL/Startup.cs @@ -6,17 +6,16 @@ using OrchardCore.ContentManagement.GraphQL.Queries; using OrchardCore.Modules; -namespace OrchardCore.ContentLocalization.GraphQL +namespace OrchardCore.ContentLocalization.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddInputObjectGraphType(); - services.AddObjectGraphType(); - services.AddTransient(); - services.AddWhereInputIndexPropertyProvider(); - } + services.AddInputObjectGraphType(); + services.AddObjectGraphType(); + services.AddTransient(); + services.AddWhereInputIndexPropertyProvider(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/ContentLocalizationPartHandlerCoordinator.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/ContentLocalizationPartHandlerCoordinator.cs index fc9b0025af7..c78de54b22c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/ContentLocalizationPartHandlerCoordinator.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/ContentLocalizationPartHandlerCoordinator.cs @@ -5,68 +5,67 @@ using OrchardCore.ContentManagement.Metadata; using OrchardCore.Modules; -namespace OrchardCore.ContentLocalization.Handlers +namespace OrchardCore.ContentLocalization.Handlers; + +internal sealed class ContentLocalizationPartHandlerCoordinator : ContentLocalizationHandlerBase { - internal sealed class ContentLocalizationPartHandlerCoordinator : ContentLocalizationHandlerBase - { - private readonly ITypeActivatorFactory _contentPartFactory; - private readonly IEnumerable _partHandlers; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ILogger _logger; + private readonly ITypeActivatorFactory _contentPartFactory; + private readonly IEnumerable _partHandlers; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ILogger _logger; + + public ContentLocalizationPartHandlerCoordinator( - public ContentLocalizationPartHandlerCoordinator( + ITypeActivatorFactory contentPartFactory, + IEnumerable partHandlers, + IContentDefinitionManager contentDefinitionManager, + ILogger logger + ) + { + _contentPartFactory = contentPartFactory; + _partHandlers = partHandlers; + _contentDefinitionManager = contentDefinitionManager; + _logger = logger; + } - ITypeActivatorFactory contentPartFactory, - IEnumerable partHandlers, - IContentDefinitionManager contentDefinitionManager, - ILogger logger - ) + public override async Task LocalizingAsync(LocalizationContentContext context) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); + if (contentTypeDefinition == null) { - _contentPartFactory = contentPartFactory; - _partHandlers = partHandlers; - _contentDefinitionManager = contentDefinitionManager; - _logger = logger; + return; } - public override async Task LocalizingAsync(LocalizationContentContext context) + foreach (var typePartDefinition in contentTypeDefinition.Parts) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - if (contentTypeDefinition == null) - { - return; - } + var partName = typePartDefinition.PartDefinition.Name; + var activator = _contentPartFactory.GetTypeActivator(partName); + var part = context.ContentItem.Get(activator.Type, typePartDefinition.Name) as ContentPart; - foreach (var typePartDefinition in contentTypeDefinition.Parts) + if (part != null) { - var partName = typePartDefinition.PartDefinition.Name; - var activator = _contentPartFactory.GetTypeActivator(partName); - var part = context.ContentItem.Get(activator.Type, typePartDefinition.Name) as ContentPart; - - if (part != null) - { - await _partHandlers.InvokeAsync((handler, context, part) => handler.LocalizingAsync(context, part), context, part, _logger); - } + await _partHandlers.InvokeAsync((handler, context, part) => handler.LocalizingAsync(context, part), context, part, _logger); } } + } - public override async Task LocalizedAsync(LocalizationContentContext context) + public override async Task LocalizedAsync(LocalizationContentContext context) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); + if (contentTypeDefinition == null) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - if (contentTypeDefinition == null) - { - return; - } + return; + } - foreach (var typePartDefinition in contentTypeDefinition.Parts) - { - var partName = typePartDefinition.PartDefinition.Name; - var activator = _contentPartFactory.GetTypeActivator(partName); - var part = context.ContentItem.Get(activator.Type, typePartDefinition.Name) as ContentPart; + foreach (var typePartDefinition in contentTypeDefinition.Parts) + { + var partName = typePartDefinition.PartDefinition.Name; + var activator = _contentPartFactory.GetTypeActivator(partName); + var part = context.ContentItem.Get(activator.Type, typePartDefinition.Name) as ContentPart; - if (part != null) - { - await _partHandlers.InvokeAsync((handler, context, part) => handler.LocalizedAsync(context, part), context, part, _logger); - } + if (part != null) + { + await _partHandlers.InvokeAsync((handler, context, part) => handler.LocalizedAsync(context, part), context, part, _logger); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/LocalizationPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/LocalizationPartHandler.cs index 3179c8b6014..1a6c632091d 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/LocalizationPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Handlers/LocalizationPartHandler.cs @@ -7,94 +7,93 @@ using OrchardCore.Entities; using OrchardCore.Localization; -namespace OrchardCore.ContentLocalization.Handlers +namespace OrchardCore.ContentLocalization.Handlers; + +public class LocalizationPartHandler : ContentPartHandler { - public class LocalizationPartHandler : ContentPartHandler + private readonly ILocalizationEntries _entries; + private readonly IIdGenerator _idGenerator; + private readonly ILocalizationService _localizationService; + + public LocalizationPartHandler( + ILocalizationEntries entries, + IIdGenerator idGenerator, + ILocalizationService localizationService) { - private readonly ILocalizationEntries _entries; - private readonly IIdGenerator _idGenerator; - private readonly ILocalizationService _localizationService; + _entries = entries; + _idGenerator = idGenerator; + _localizationService = localizationService; + } - public LocalizationPartHandler( - ILocalizationEntries entries, - IIdGenerator idGenerator, - ILocalizationService localizationService) + public override async Task CreatingAsync(CreateContentContext context, LocalizationPart part) + { + if (string.IsNullOrEmpty(part.LocalizationSet)) { - _entries = entries; - _idGenerator = idGenerator; - _localizationService = localizationService; + context.ContentItem.Alter(p => + p.LocalizationSet = _idGenerator.GenerateUniqueId() + ); } - public override async Task CreatingAsync(CreateContentContext context, LocalizationPart part) + if (string.IsNullOrEmpty(part.Culture)) { - if (string.IsNullOrEmpty(part.LocalizationSet)) - { - context.ContentItem.Alter(p => - p.LocalizationSet = _idGenerator.GenerateUniqueId() - ); - } - - if (string.IsNullOrEmpty(part.Culture)) - { - await context.ContentItem.AlterAsync(async p => - p.Culture = await _localizationService.GetDefaultCultureAsync() - ); - } - } - - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, LocalizationPart part) - { - return context.ForAsync(cultureAspect => - { - if (part.Culture != null) - { - cultureAspect.Culture = CultureInfo.GetCultureInfo(part.Culture); - cultureAspect.HasCulture = true; - } - - return Task.CompletedTask; - }); + await context.ContentItem.AlterAsync(async p => + p.Culture = await _localizationService.GetDefaultCultureAsync() + ); } + } - public override Task PublishedAsync(PublishContentContext context, LocalizationPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, LocalizationPart part) + { + return context.ForAsync(cultureAspect => { - if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null) + if (part.Culture != null) { - // Update entries from the index table after the session is committed. - return _entries.UpdateEntriesAsync(); + cultureAspect.Culture = CultureInfo.GetCultureInfo(part.Culture); + cultureAspect.HasCulture = true; } return Task.CompletedTask; - } + }); + } - public override Task UnpublishedAsync(PublishContentContext context, LocalizationPart part) + public override Task PublishedAsync(PublishContentContext context, LocalizationPart part) + { + if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null) { - if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null) - { - // Update entries from the index table after the session is committed. - return _entries.UpdateEntriesAsync(); - } - - return Task.CompletedTask; + // Update entries from the index table after the session is committed. + return _entries.UpdateEntriesAsync(); } - public override Task RemovedAsync(RemoveContentContext context, LocalizationPart part) - { - if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null && context.NoActiveVersionLeft) - { - // Update entries from the index table after the session is committed. - return _entries.UpdateEntriesAsync(); - } + return Task.CompletedTask; + } - return Task.CompletedTask; + public override Task UnpublishedAsync(PublishContentContext context, LocalizationPart part) + { + if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null) + { + // Update entries from the index table after the session is committed. + return _entries.UpdateEntriesAsync(); } - public override Task CloningAsync(CloneContentContext context, LocalizationPart part) + return Task.CompletedTask; + } + + public override Task RemovedAsync(RemoveContentContext context, LocalizationPart part) + { + if (!string.IsNullOrWhiteSpace(part.LocalizationSet) && part.Culture != null && context.NoActiveVersionLeft) { - var clonedPart = context.CloneContentItem.As(); - clonedPart.LocalizationSet = string.Empty; - clonedPart.Apply(); - return Task.CompletedTask; + // Update entries from the index table after the session is committed. + return _entries.UpdateEntriesAsync(); } + + return Task.CompletedTask; + } + + public override Task CloningAsync(CloneContentContext context, LocalizationPart part) + { + var clonedPart = context.CloneContentItem.As(); + clonedPart.LocalizationSet = string.Empty; + clonedPart.Apply(); + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Indexing/LocalizationPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Indexing/LocalizationPartIndexHandler.cs index d5a572d55f4..f2696e72530 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Indexing/LocalizationPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Indexing/LocalizationPartIndexHandler.cs @@ -2,21 +2,20 @@ using OrchardCore.ContentLocalization.Models; using OrchardCore.Indexing; -namespace OrchardCore.ContentLocalization.Indexing +namespace OrchardCore.ContentLocalization.Indexing; + +public class LocalizationPartIndexHandler : ContentPartIndexHandler { - public class LocalizationPartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(LocalizationPart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(LocalizationPart part, BuildPartIndexContext context) - { - var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key + ".LocalizationSet", part.LocalizationSet, options); - context.DocumentIndex.Set(key + ".Culture", part.Culture?.ToLowerInvariant(), options); - } + var options = DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key + ".LocalizationSet", part.LocalizationSet, options); + context.DocumentIndex.Set(key + ".Culture", part.Culture?.ToLowerInvariant(), options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/ContentLocalizationFilter.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/ContentLocalizationFilter.cs index 70c6f318c6c..945ce71fe25 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/ContentLocalizationFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/ContentLocalizationFilter.cs @@ -4,40 +4,39 @@ using Fluid.Values; using OrchardCore.Liquid; -namespace OrchardCore.ContentLocalization.Liquid +namespace OrchardCore.ContentLocalization.Liquid; + +public class ContentLocalizationFilter : ILiquidFilter { - public class ContentLocalizationFilter : ILiquidFilter + private readonly IContentLocalizationManager _contentLocalizationManager; + + public ContentLocalizationFilter(IContentLocalizationManager contentLocalizationManager) + { + _contentLocalizationManager = contentLocalizationManager; + } + + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) { - private readonly IContentLocalizationManager _contentLocalizationManager; + var locale = arguments.At(0).ToStringValue(); - public ContentLocalizationFilter(IContentLocalizationManager contentLocalizationManager) + if (arguments.At(0).IsNil()) { - _contentLocalizationManager = contentLocalizationManager; + locale = ctx.CultureInfo.Name; } - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + if (input.Type == FluidValues.Array) { - var locale = arguments.At(0).ToStringValue(); + // List of content item ids - if (arguments.At(0).IsNil()) - { - locale = ctx.CultureInfo.Name; - } + var localizationSets = input.Enumerate(ctx).Select(x => x.ToStringValue()).ToArray(); - if (input.Type == FluidValues.Array) - { - // List of content item ids - - var localizationSets = input.Enumerate(ctx).Select(x => x.ToStringValue()).ToArray(); - - return FluidValue.Create(await _contentLocalizationManager.GetItemsForSetsAsync(localizationSets, locale), ctx.Options); - } - else - { - var localizationSet = input.ToStringValue(); + return FluidValue.Create(await _contentLocalizationManager.GetItemsForSetsAsync(localizationSets, locale), ctx.Options); + } + else + { + var localizationSet = input.ToStringValue(); - return FluidValue.Create(await _contentLocalizationManager.GetContentItemAsync(localizationSet, locale), ctx.Options); - } + return FluidValue.Create(await _contentLocalizationManager.GetContentItemAsync(localizationSet, locale), ctx.Options); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/SwitchCultureUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/SwitchCultureUrlFilter.cs index 945a3244aaa..583ea5d6bf6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/SwitchCultureUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Liquid/SwitchCultureUrlFilter.cs @@ -7,37 +7,36 @@ using Microsoft.AspNetCore.Mvc.Routing; using OrchardCore.Liquid; -namespace OrchardCore.ContentLocalization.Liquid +namespace OrchardCore.ContentLocalization.Liquid; + +public class SwitchCultureUrlFilter : ILiquidFilter { - public class SwitchCultureUrlFilter : ILiquidFilter - { - private readonly IUrlHelperFactory _urlHelperFactory; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IHttpContextAccessor _httpContextAccessor; - public SwitchCultureUrlFilter(IUrlHelperFactory urlHelperFactory, IHttpContextAccessor httpContextAccessor) - { - _urlHelperFactory = urlHelperFactory; - _httpContextAccessor = httpContextAccessor; - } + public SwitchCultureUrlFilter(IUrlHelperFactory urlHelperFactory, IHttpContextAccessor httpContextAccessor) + { + _urlHelperFactory = urlHelperFactory; + _httpContextAccessor = httpContextAccessor; + } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) - { - var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + { + var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); - var request = _httpContextAccessor.HttpContext?.Request - ?? throw new ArgumentException("HttpRequest missing while invoking 'switch_culture_url'"); + var request = _httpContextAccessor.HttpContext?.Request + ?? throw new ArgumentException("HttpRequest missing while invoking 'switch_culture_url'"); - var targetCulture = input.ToStringValue(); + var targetCulture = input.ToStringValue(); - var url = urlHelper.RouteUrl("RedirectToLocalizedContent", - new - { - area = "OrchardCore.ContentLocalization", - targetCulture, - contentItemUrl = request.Path.Value, - queryStringValue = request.QueryString.Value, - }); - return new ValueTask(FluidValue.Create(url, context.Options)); - } + var url = urlHelper.RouteUrl("RedirectToLocalizedContent", + new + { + area = "OrchardCore.ContentLocalization", + targetCulture, + contentItemUrl = request.Path.Value, + queryStringValue = request.QueryString.Value, + }); + return new ValueTask(FluidValue.Create(url, context.Options)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Migrations.cs index cf7f363d7e9..4154353993b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Migrations.cs @@ -9,93 +9,92 @@ using YesSql; using YesSql.Sql; -namespace OrchardCore.ContentLocalization.Records +namespace OrchardCore.ContentLocalization.Records; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(LocalizationPart), builder => builder - .Attachable() - .WithDescription("Provides a way to create localized version of content.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(LocalizationPart), builder => builder + .Attachable() + .WithDescription("Provides a way to create localized version of content.")); - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("LocalizationSet", col => col.WithLength(26)) - .Column("Culture", col => col.WithLength(16)) - .Column("ContentItemId", c => c.WithLength(26)) - .Column("Published") - .Column("Latest") - ); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("LocalizationSet", col => col.WithLength(26)) + .Column("Culture", col => col.WithLength(16)) + .Column("ContentItemId", c => c.WithLength(26)) + .Column("Published") + .Column("Latest") + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LocalizationPartIndex_DocumentId", - "DocumentId", - "LocalizationSet", - "Culture", - "ContentItemId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LocalizationPartIndex_DocumentId", + "DocumentId", + "LocalizationSet", + "Culture", + "ContentItemId", + "Published", + "Latest") + ); - // Shortcut other migration steps on new content definition schemas. - return 4; - } + // Shortcut other migration steps on new content definition schemas. + return 4; + } - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn(nameof(LocalizedContentItemIndex.Published))); + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn(nameof(LocalizedContentItemIndex.Published))); - return 2; - } + return 2; + } - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn(nameof(LocalizedContentItemIndex.Latest)) - ); + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn(nameof(LocalizedContentItemIndex.Latest)) + ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LocalizationPartIndex_DocumentId", - "DocumentId", - "LocalizationSet", - "Culture", - "ContentItemId", - "Published", - "Latest") - ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LocalizationPartIndex_DocumentId", + "DocumentId", + "LocalizationSet", + "Culture", + "ContentItemId", + "Published", + "Latest") + ); - return 3; - } + return 3; + } - // Migrate null LocalizedContentItemIndex Latest column. + // Migrate null LocalizedContentItemIndex Latest column. #pragma warning disable CA1822 // Mark members as static - public int UpdateFrom3() + public int UpdateFrom3() #pragma warning restore CA1822 // Mark members as static + { + // Defer this until after the subsequent migrations have succeeded as the schema has changed. + ShellScope.AddDeferredTask(async scope => { - // Defer this until after the subsequent migrations have succeeded as the schema has changed. - ShellScope.AddDeferredTask(async scope => - { - var session = scope.ServiceProvider.GetRequiredService(); - var localizedContentItems = await session.Query().ListAsync(); + var session = scope.ServiceProvider.GetRequiredService(); + var localizedContentItems = await session.Query().ListAsync(); - foreach (var localizedContentItem in localizedContentItems) - { - localizedContentItem.Latest = localizedContentItem.ContentItem.Latest; - await session.SaveAsync(localizedContentItem); - } - }); + foreach (var localizedContentItem in localizedContentItems) + { + localizedContentItem.Latest = localizedContentItem.ContentItem.Latest; + await session.SaveAsync(localizedContentItem); + } + }); - return 4; - } + return 4; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentCulturePickerSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentCulturePickerSettings.cs index fc858228fb4..92f6223d4f7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentCulturePickerSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentCulturePickerSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentLocalization.Models +namespace OrchardCore.ContentLocalization.Models; + +public class ContentCulturePickerSettings { - public class ContentCulturePickerSettings - { - public bool RedirectToHomepage { get; set; } - public bool SetCookie { get; set; } = true; - } + public bool RedirectToHomepage { get; set; } + public bool SetCookie { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentRequestCultureProviderSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentRequestCultureProviderSettings.cs index a135b81baf7..809bc62607a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentRequestCultureProviderSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Models/ContentRequestCultureProviderSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentLocalization.Models +namespace OrchardCore.ContentLocalization.Models; + +public class ContentRequestCultureProviderSettings { - public class ContentRequestCultureProviderSettings - { - public bool SetCookie { get; set; } - } + public bool SetCookie { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Security/LocalizeContentAuthorizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Security/LocalizeContentAuthorizationHandler.cs index a76736462c7..99980d5015e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Security/LocalizeContentAuthorizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Security/LocalizeContentAuthorizationHandler.cs @@ -7,80 +7,79 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.ContentLocalization.Security +namespace OrchardCore.ContentLocalization.Security; + +public class LocalizeContentAuthorizationHandler : AuthorizationHandler { - public class LocalizeContentAuthorizationHandler : AuthorizationHandler + private readonly IServiceProvider _serviceProvider; + private IAuthorizationService _authorizationService; + + public LocalizeContentAuthorizationHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; - private IAuthorizationService _authorizationService; + _serviceProvider = serviceProvider; + } - public LocalizeContentAuthorizationHandler(IServiceProvider serviceProvider) + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _serviceProvider = serviceProvider; + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + if (context.Resource == null) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } - - if (context.Resource == null) - { - return; - } - - var contentItem = context.Resource as ContentItem; - - Permission permission = null; - - if (contentItem != null) - { - if (OwnerVariationExists(requirement.Permission) && HasOwnership(context.User, contentItem)) - { - permission = GetOwnerVariation(requirement.Permission); - } - } + return; + } - if (permission == null) - { - return; - } + var contentItem = context.Resource as ContentItem; - // Lazy load to prevent circular dependencies - _authorizationService ??= _serviceProvider.GetService(); + Permission permission = null; - if (await _authorizationService.AuthorizeAsync(context.User, permission)) + if (contentItem != null) + { + if (OwnerVariationExists(requirement.Permission) && HasOwnership(context.User, contentItem)) { - context.Succeed(requirement); + permission = GetOwnerVariation(requirement.Permission); } } - private static Permission GetOwnerVariation(Permission permission) + if (permission == null) { - if (permission.Name == Permissions.LocalizeContent.Name) - { - return Permissions.LocalizeOwnContent; - } - - return null; + return; } - private static bool HasOwnership(ClaimsPrincipal user, ContentItem content) + // Lazy load to prevent circular dependencies + _authorizationService ??= _serviceProvider.GetService(); + + if (await _authorizationService.AuthorizeAsync(context.User, permission)) { - if (user == null || content == null) - { - return false; - } + context.Succeed(requirement); + } + } - return user.FindFirstValue(ClaimTypes.NameIdentifier) == content.Owner; + private static Permission GetOwnerVariation(Permission permission) + { + if (permission.Name == Permissions.LocalizeContent.Name) + { + return Permissions.LocalizeOwnContent; } - private static bool OwnerVariationExists(Permission permission) + return null; + } + + private static bool HasOwnership(ClaimsPrincipal user, ContentItem content) + { + if (user == null || content == null) { - return GetOwnerVariation(permission) != null; + return false; } + + return user.FindFirstValue(ClaimTypes.NameIdentifier) == content.Owner; + } + + private static bool OwnerVariationExists(Permission permission) + { + return GetOwnerVariation(permission) != null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ServiceCollectionExtensions.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ServiceCollectionExtensions.cs index 763badabe2e..7bae62697ad 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ServiceCollectionExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ServiceCollectionExtensions.cs @@ -10,26 +10,25 @@ using OrchardCore.Data; using OrchardCore.Data.Migration; -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +public static class ServiceCollectionExtensions { - public static class ServiceCollectionExtensions + public static IServiceCollection AddContentLocalization(this IServiceCollection services) { - public static IServiceCollection AddContentLocalization(this IServiceCollection services) - { - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.TryAddScoped(); + services.TryAddScoped(); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); - services.AddDataMigration(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); - return services; - } + return services; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ContentCulturePickerService.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ContentCulturePickerService.cs index 6ef78e2e22e..d7d2497af71 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ContentCulturePickerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ContentCulturePickerService.cs @@ -8,109 +8,108 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Settings; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public class ContentCulturePickerService : IContentCulturePickerService { - public class ContentCulturePickerService : IContentCulturePickerService + private readonly IAutorouteEntries _autorouteEntries; + private readonly ILocalizationEntries _localizationEntries; + private readonly ISiteService _siteService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly CulturePickerOptions _culturePickerOptions; + + public ContentCulturePickerService( + IAutorouteEntries autorouteEntries, + ILocalizationEntries localizationEntries, + ISiteService siteService, + IHttpContextAccessor httpContextAccessor, + IOptions culturePickerOptions) { - private readonly IAutorouteEntries _autorouteEntries; - private readonly ILocalizationEntries _localizationEntries; - private readonly ISiteService _siteService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly CulturePickerOptions _culturePickerOptions; - - public ContentCulturePickerService( - IAutorouteEntries autorouteEntries, - ILocalizationEntries localizationEntries, - ISiteService siteService, - IHttpContextAccessor httpContextAccessor, - IOptions culturePickerOptions) - { - _autorouteEntries = autorouteEntries; - _localizationEntries = localizationEntries; - _siteService = siteService; - _httpContextAccessor = httpContextAccessor; - _culturePickerOptions = culturePickerOptions.Value; - } + _autorouteEntries = autorouteEntries; + _localizationEntries = localizationEntries; + _siteService = siteService; + _httpContextAccessor = httpContextAccessor; + _culturePickerOptions = culturePickerOptions.Value; + } - public async Task GetContentItemIdFromRouteAsync(PathString url) + public async Task GetContentItemIdFromRouteAsync(PathString url) + { + if (!url.HasValue) { - if (!url.HasValue) - { - url = "/"; - } + url = "/"; + } - string contentItemId = null; + string contentItemId = null; - if (url == "/") - { - // get contentItemId from homeroute - var siteSettings = await _siteService.GetSiteSettingsAsync(); - contentItemId = siteSettings.HomeRoute["contentItemId"]?.ToString(); - } - else - { - // Try to get from autorouteEntries. - // This should not consider contained items, so will redirect to the parent item. - (var found, var entry) = await _autorouteEntries.TryGetEntryByPathAsync(url.Value); - - if (found) - { - contentItemId = entry.ContentItemId; - } - } + if (url == "/") + { + // get contentItemId from homeroute + var siteSettings = await _siteService.GetSiteSettingsAsync(); + contentItemId = siteSettings.HomeRoute["contentItemId"]?.ToString(); + } + else + { + // Try to get from autorouteEntries. + // This should not consider contained items, so will redirect to the parent item. + (var found, var entry) = await _autorouteEntries.TryGetEntryByPathAsync(url.Value); - if (string.IsNullOrEmpty(contentItemId)) + if (found) { - return null; + contentItemId = entry.ContentItemId; } + } - return contentItemId; + if (string.IsNullOrEmpty(contentItemId)) + { + return null; } - public async Task GetLocalizationFromRouteAsync(PathString url) + return contentItemId; + } + + public async Task GetLocalizationFromRouteAsync(PathString url) + { + var contentItemId = await GetContentItemIdFromRouteAsync(url); + + if (!string.IsNullOrEmpty(contentItemId)) { - var contentItemId = await GetContentItemIdFromRouteAsync(url); + (var found, var localization) = await _localizationEntries.TryGetLocalizationAsync(contentItemId); - if (!string.IsNullOrEmpty(contentItemId)) + if (found) { - (var found, var localization) = await _localizationEntries.TryGetLocalizationAsync(contentItemId); - - if (found) - { - return localization; - } + return localization; } - - return null; } - public async Task> GetLocalizationsFromRouteAsync(PathString url) + return null; + } + + public async Task> GetLocalizationsFromRouteAsync(PathString url) + { + var contentItemId = await GetContentItemIdFromRouteAsync(url); + + if (!string.IsNullOrEmpty(contentItemId)) { - var contentItemId = await GetContentItemIdFromRouteAsync(url); + (var found, var localization) = await _localizationEntries.TryGetLocalizationAsync(contentItemId); - if (!string.IsNullOrEmpty(contentItemId)) + if (found) { - (var found, var localization) = await _localizationEntries.TryGetLocalizationAsync(contentItemId); - - if (found) - { - return await _localizationEntries.GetLocalizationsAsync(localization.LocalizationSet); - } + return await _localizationEntries.GetLocalizationsAsync(localization.LocalizationSet); } - - return []; } - public void SetContentCulturePickerCookie(string targetCulture) - { - var response = _httpContextAccessor.HttpContext.Response; - // Set the cookie to handle redirecting to a custom controller - response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName); - response.Cookies.Append( - CookieRequestCultureProvider.DefaultCookieName, - CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(targetCulture)), - new CookieOptions { Expires = DateTime.UtcNow.AddDays(_culturePickerOptions.CookieLifeTime) } - ); - } + return []; + } + + public void SetContentCulturePickerCookie(string targetCulture) + { + var response = _httpContextAccessor.HttpContext.Response; + // Set the cookie to handle redirecting to a custom controller + response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName); + response.Cookies.Append( + CookieRequestCultureProvider.DefaultCookieName, + CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(targetCulture)), + new CookieOptions { Expires = DateTime.UtcNow.AddDays(_culturePickerOptions.CookieLifeTime) } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/IContentCulturePickerService.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/IContentCulturePickerService.cs index 193ed9ef8b1..1e5ea8ddd5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/IContentCulturePickerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/IContentCulturePickerService.cs @@ -3,28 +3,27 @@ using Microsoft.AspNetCore.Http; using OrchardCore.ContentLocalization.Models; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public interface IContentCulturePickerService { - public interface IContentCulturePickerService - { - /// - /// Get the ContentItemId that matches the url. - /// - /// ContentItemId or null if not found. - Task GetContentItemIdFromRouteAsync(PathString url); - /// - /// Get the Localization of the ContentItem from an url. - /// - /// Culture or null if not found. - Task GetLocalizationFromRouteAsync(PathString url); - /// - /// Get the Localizations of the LocalizationSet from a URL. - /// - /// List of Localization for the url or null if not found. - Task> GetLocalizationsFromRouteAsync(PathString url); - /// - /// Sets the culture cookie to the target culture. - /// - void SetContentCulturePickerCookie(string targetCulture); - } + /// + /// Get the ContentItemId that matches the url. + /// + /// ContentItemId or null if not found. + Task GetContentItemIdFromRouteAsync(PathString url); + /// + /// Get the Localization of the ContentItem from an url. + /// + /// Culture or null if not found. + Task GetLocalizationFromRouteAsync(PathString url); + /// + /// Get the Localizations of the LocalizationSet from a URL. + /// + /// List of Localization for the url or null if not found. + Task> GetLocalizationsFromRouteAsync(PathString url); + /// + /// Sets the culture cookie to the target culture. + /// + void SetContentCulturePickerCookie(string targetCulture); } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ILocalizationEntries.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ILocalizationEntries.cs index 49648ee78c7..87136dc5174 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ILocalizationEntries.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/ILocalizationEntries.cs @@ -2,12 +2,11 @@ using System.Threading.Tasks; using OrchardCore.ContentLocalization.Models; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public interface ILocalizationEntries { - public interface ILocalizationEntries - { - Task<(bool, LocalizationEntry)> TryGetLocalizationAsync(string contentItemId); - Task> GetLocalizationsAsync(string localizationSet); - Task UpdateEntriesAsync(); - } + Task<(bool, LocalizationEntry)> TryGetLocalizationAsync(string contentItemId); + Task> GetLocalizationsAsync(string localizationSet); + Task UpdateEntriesAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationEntries.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationEntries.cs index 450ca837b34..df674b1ab64 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationEntries.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationEntries.cs @@ -10,194 +10,154 @@ using OrchardCore.Environment.Shell.Scope; using YesSql; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public class LocalizationEntries : ILocalizationEntries { - public class LocalizationEntries : ILocalizationEntries - { - private readonly IVolatileDocumentManager _localizationStateManager; + private readonly IVolatileDocumentManager _localizationStateManager; + + private ImmutableDictionary _localizations = ImmutableDictionary.Empty; - private ImmutableDictionary _localizations = ImmutableDictionary.Empty; + private ImmutableDictionary> _localizationSets = + ImmutableDictionary>.Empty; - private ImmutableDictionary> _localizationSets = - ImmutableDictionary>.Empty; + private readonly SemaphoreSlim _semaphore = new(1); - private readonly SemaphoreSlim _semaphore = new(1); + private long _lastIndexId; + private string _stateIdentifier; + private bool _initialized; - private long _lastIndexId; - private string _stateIdentifier; - private bool _initialized; + public LocalizationEntries(IVolatileDocumentManager localizationStateManager) + { + _localizationStateManager = localizationStateManager; + } - public LocalizationEntries(IVolatileDocumentManager localizationStateManager) + public async Task<(bool, LocalizationEntry)> TryGetLocalizationAsync(string contentItemId) + { + await EnsureInitializedAsync(); + + if (_localizations.TryGetValue(contentItemId, out var localization)) { - _localizationStateManager = localizationStateManager; + return (true, localization); } - public async Task<(bool, LocalizationEntry)> TryGetLocalizationAsync(string contentItemId) - { - await EnsureInitializedAsync(); + return (false, localization); + } - if (_localizations.TryGetValue(contentItemId, out var localization)) - { - return (true, localization); - } + public async Task> GetLocalizationsAsync(string localizationSet) + { + await EnsureInitializedAsync(); - return (false, localization); + if (_localizationSets.TryGetValue(localizationSet, out var localizations)) + { + return localizations; } - public async Task> GetLocalizationsAsync(string localizationSet) - { - await EnsureInitializedAsync(); + return []; + } - if (_localizationSets.TryGetValue(localizationSet, out var localizations)) - { - return localizations; - } + public async Task UpdateEntriesAsync() + { + await EnsureInitializedAsync(); - return []; - } + // Update the cache with a new state and then refresh entries as it would be done on a next request. + await _localizationStateManager.UpdateAsync(new LocalizationStateDocument(), afterUpdateAsync: RefreshEntriesAsync); + } - public async Task UpdateEntriesAsync() + private async Task EnsureInitializedAsync() + { + if (!_initialized) { - await EnsureInitializedAsync(); - - // Update the cache with a new state and then refresh entries as it would be done on a next request. - await _localizationStateManager.UpdateAsync(new LocalizationStateDocument(), afterUpdateAsync: RefreshEntriesAsync); + await InitializeEntriesAsync(); } - - private async Task EnsureInitializedAsync() + else { - if (!_initialized) + var state = await _localizationStateManager.GetOrCreateImmutableAsync(); + if (_stateIdentifier != state.Identifier) { - await InitializeEntriesAsync(); - } - else - { - var state = await _localizationStateManager.GetOrCreateImmutableAsync(); - if (_stateIdentifier != state.Identifier) - { - await RefreshEntriesAsync(state); - } + await RefreshEntriesAsync(state); } } + } - protected void AddEntries(IEnumerable entries) + protected void AddEntries(IEnumerable entries) + { + foreach (var entry in entries) { - foreach (var entry in entries) + if (_localizations.ContainsKey(entry.ContentItemId)) { - if (_localizations.ContainsKey(entry.ContentItemId)) - { - continue; - } - - _localizations = _localizations.SetItem(entry.ContentItemId, entry); + continue; + } - if (_localizationSets.TryGetValue(entry.LocalizationSet, out var localizations)) - { - localizations = localizations.Add(entry); - } - else - { - localizations = [entry]; - } + _localizations = _localizations.SetItem(entry.ContentItemId, entry); - _localizationSets = _localizationSets.SetItem(entry.LocalizationSet, localizations); + if (_localizationSets.TryGetValue(entry.LocalizationSet, out var localizations)) + { + localizations = localizations.Add(entry); } - } - - protected void RemoveEntries(IEnumerable entries) - { - foreach (var entry in entries) + else { - if (!_localizations.ContainsKey(entry.ContentItemId)) - { - continue; - } - - _localizations = _localizations.Remove(entry.ContentItemId); - - if (_localizationSets.TryGetValue(entry.LocalizationSet, out var localizations)) - { - localizations = localizations.RemoveAll(l => l.Culture == entry.Culture); - _localizationSets = _localizationSets.SetItem(entry.LocalizationSet, localizations); - } + localizations = [entry]; } + + _localizationSets = _localizationSets.SetItem(entry.LocalizationSet, localizations); } + } - private async Task RefreshEntriesAsync(LocalizationStateDocument state) + protected void RemoveEntries(IEnumerable entries) + { + foreach (var entry in entries) { - if (_stateIdentifier == state.Identifier) + if (!_localizations.ContainsKey(entry.ContentItemId)) { - return; + continue; } - await _semaphore.WaitAsync(); - try - { - if (_stateIdentifier != state.Identifier) - { - var indexes = await Session - .QueryIndex(i => i.Id > _lastIndexId) - .OrderBy(i => i.Id) - .ListAsync(); - - // A draft is indexed to check for conflicts, and to remove an entry, but only if an item is unpublished, - // so only if the entry 'DocumentId' matches, this because when a draft is saved more than once, the index - // is not updated for the published version that may be already scanned, so the entry may not be re-added. - - var entriesToRemove = indexes - .Where(i => !i.Published || i.Culture == null) - .SelectMany(i => _localizations.Values.Where(e => - // The item was removed. - ((!i.Published && !i.Latest) || - // The part was removed. - (i.Culture == null && i.Published) || - // The item was unpublished. - (!i.Published && e.DocumentId == i.DocumentId)) && - (e.ContentItemId == i.ContentItemId))); - - var entriesToAdd = indexes. - Where(i => i.Published && i.Culture != null) - .Select(i => new LocalizationEntry - { - DocumentId = i.DocumentId, - ContentItemId = i.ContentItemId, - LocalizationSet = i.LocalizationSet, - Culture = i.Culture.ToLowerInvariant() - }); - - RemoveEntries(entriesToRemove); - AddEntries(entriesToAdd); - - _lastIndexId = indexes.LastOrDefault()?.Id ?? 0; - _stateIdentifier = state.Identifier; - } - } - finally + _localizations = _localizations.Remove(entry.ContentItemId); + + if (_localizationSets.TryGetValue(entry.LocalizationSet, out var localizations)) { - _semaphore.Release(); + localizations = localizations.RemoveAll(l => l.Culture == entry.Culture); + _localizationSets = _localizationSets.SetItem(entry.LocalizationSet, localizations); } } + } - protected virtual async Task InitializeEntriesAsync() + private async Task RefreshEntriesAsync(LocalizationStateDocument state) + { + if (_stateIdentifier == state.Identifier) { - if (_initialized) - { - return; - } + return; + } - await _semaphore.WaitAsync(); - try + await _semaphore.WaitAsync(); + try + { + if (_stateIdentifier != state.Identifier) { - if (!_initialized) - { - var state = await _localizationStateManager.GetOrCreateImmutableAsync(); - - var indexes = await Session - .QueryIndex(i => i.Published && i.Culture != null) - .OrderBy(i => i.Id) - .ListAsync(); - - var entries = indexes.Select(i => new LocalizationEntry + var indexes = await Session + .QueryIndex(i => i.Id > _lastIndexId) + .OrderBy(i => i.Id) + .ListAsync(); + + // A draft is indexed to check for conflicts, and to remove an entry, but only if an item is unpublished, + // so only if the entry 'DocumentId' matches, this because when a draft is saved more than once, the index + // is not updated for the published version that may be already scanned, so the entry may not be re-added. + + var entriesToRemove = indexes + .Where(i => !i.Published || i.Culture == null) + .SelectMany(i => _localizations.Values.Where(e => + // The item was removed. + ((!i.Published && !i.Latest) || + // The part was removed. + (i.Culture == null && i.Published) || + // The item was unpublished. + (!i.Published && e.DocumentId == i.DocumentId)) && + (e.ContentItemId == i.ContentItemId))); + + var entriesToAdd = indexes. + Where(i => i.Published && i.Culture != null) + .Select(i => new LocalizationEntry { DocumentId = i.DocumentId, ContentItemId = i.ContentItemId, @@ -205,20 +165,59 @@ protected virtual async Task InitializeEntriesAsync() Culture = i.Culture.ToLowerInvariant() }); - AddEntries(entries); + RemoveEntries(entriesToRemove); + AddEntries(entriesToAdd); - _lastIndexId = indexes.LastOrDefault()?.Id ?? 0; - _stateIdentifier = state.Identifier; - - _initialized = true; - } + _lastIndexId = indexes.LastOrDefault()?.Id ?? 0; + _stateIdentifier = state.Identifier; } - finally + } + finally + { + _semaphore.Release(); + } + } + + protected virtual async Task InitializeEntriesAsync() + { + if (_initialized) + { + return; + } + + await _semaphore.WaitAsync(); + try + { + if (!_initialized) { - _semaphore.Release(); + var state = await _localizationStateManager.GetOrCreateImmutableAsync(); + + var indexes = await Session + .QueryIndex(i => i.Published && i.Culture != null) + .OrderBy(i => i.Id) + .ListAsync(); + + var entries = indexes.Select(i => new LocalizationEntry + { + DocumentId = i.DocumentId, + ContentItemId = i.ContentItemId, + LocalizationSet = i.LocalizationSet, + Culture = i.Culture.ToLowerInvariant() + }); + + AddEntries(entries); + + _lastIndexId = indexes.LastOrDefault()?.Id ?? 0; + _stateIdentifier = state.Identifier; + + _initialized = true; } } - - private static ISession Session => ShellScope.Services.GetRequiredService(); + finally + { + _semaphore.Release(); + } } + + private static ISession Session => ShellScope.Services.GetRequiredService(); } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilter.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilter.cs index 69cc22a8d3b..91a048e6439 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilter.cs @@ -11,35 +11,34 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public class LocalizationPartContentsAdminListFilter : IContentsAdminListFilter { - public class LocalizationPartContentsAdminListFilter : IContentsAdminListFilter + private readonly IContentDefinitionManager _contentDefinitionManager; + + public LocalizationPartContentsAdminListFilter(IContentDefinitionManager contentDefinitionManager) { - private readonly IContentDefinitionManager _contentDefinitionManager; + _contentDefinitionManager = contentDefinitionManager; + } - public LocalizationPartContentsAdminListFilter(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) + { + var viewModel = new LocalizationContentsAdminFilterViewModel(); + await updater.TryUpdateModelAsync(viewModel, "Localization"); - public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) + // Show localization content items + // This is intended to be used by adding ?Localization.ShowLocalizedContentTypes to an AdminMenu url. + if (viewModel.ShowLocalizedContentTypes) { - var viewModel = new LocalizationContentsAdminFilterViewModel(); - await updater.TryUpdateModelAsync(viewModel, "Localization"); - - // Show localization content items - // This is intended to be used by adding ?Localization.ShowLocalizedContentTypes to an AdminMenu url. - if (viewModel.ShowLocalizedContentTypes) - { - var localizedTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(x => - x.Parts.Any(p => - p.PartDefinition.Name == nameof(LocalizationPart))) - .Select(x => x.Name); + var localizedTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(x => + x.Parts.Any(p => + p.PartDefinition.Name == nameof(LocalizationPart))) + .Select(x => x.Name); - query.With(x => x.ContentType.IsIn(localizedTypes)); + query.With(x => x.ContentType.IsIn(localizedTypes)); - } } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilterProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilterProvider.cs index efe2737a967..dab20e98358 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilterProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationPartContentsAdminListFilterProvider.cs @@ -4,33 +4,32 @@ using OrchardCore.Contents.Services; using YesSql.Filters.Query; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public class LocalizationPartContentsAdminListFilterProvider : IContentsAdminListFilterProvider { - public class LocalizationPartContentsAdminListFilterProvider : IContentsAdminListFilterProvider + public void Build(QueryEngineBuilder builder) { - public void Build(QueryEngineBuilder builder) - { - builder - .WithNamedTerm("culture", builder => builder - .OneCondition((val, query) => + builder + .WithNamedTerm("culture", builder => builder + .OneCondition((val, query) => + { + if (!string.IsNullOrEmpty(val)) { - if (!string.IsNullOrEmpty(val)) - { - query.With(i => (i.Published || i.Latest) && i.Culture == val); - } + query.With(i => (i.Published || i.Latest) && i.Culture == val); + } - return query; - }) - .MapTo((val, model) => model.SelectedCulture = val) - .MapFrom((model) => + return query; + }) + .MapTo((val, model) => model.SelectedCulture = val) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.SelectedCulture)) { - if (!string.IsNullOrEmpty(model.SelectedCulture)) - { - return (true, model.SelectedCulture); - } - return (false, string.Empty); - }) - ); - } + return (true, model.SelectedCulture); + } + return (false, string.Empty); + }) + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationStateDocument.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationStateDocument.cs index 37f14d5a46d..4aac0dc2b6b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationStateDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Services/LocalizationStateDocument.cs @@ -1,8 +1,7 @@ using OrchardCore.Data.Documents; -namespace OrchardCore.ContentLocalization.Services +namespace OrchardCore.ContentLocalization.Services; + +public class LocalizationStateDocument : Document { - public class LocalizationStateDocument : Document - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/LocalizedContentItemsQueryProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/LocalizedContentItemsQueryProvider.cs index 3d8488c96a0..a77e0717bb8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/LocalizedContentItemsQueryProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/LocalizedContentItemsQueryProvider.cs @@ -12,114 +12,113 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.ContentLocalization.Sitemaps +namespace OrchardCore.ContentLocalization.Sitemaps; + +public class LocalizedContentItemsQueryProvider : IContentItemsQueryProvider { - public class LocalizedContentItemsQueryProvider : IContentItemsQueryProvider + private readonly ISession _session; + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + private readonly ILocalizationService _localizationService; + + public LocalizedContentItemsQueryProvider( + ISession session, + IRouteableContentTypeCoordinator routeableContentTypeCoordinator, + ILocalizationService localizationService + ) + { + _session = session; + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + _localizationService = localizationService; + } + + public async Task GetContentItemsAsync(ContentTypesSitemapSource source, ContentItemsQueryContext queryContext) { - private readonly ISession _session; - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; - private readonly ILocalizationService _localizationService; - - public LocalizedContentItemsQueryProvider( - ISession session, - IRouteableContentTypeCoordinator routeableContentTypeCoordinator, - ILocalizationService localizationService - ) + var routeableContentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); + + if (source.IndexAll) { - _session = session; - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; - _localizationService = localizationService; - } + // Assumption here is that at least one content type will be localized. + var ctdNames = routeableContentTypeDefinitions.Select(ctd => ctd.Name); + + var queryResults = await _session.Query() + .With(x => x.Published && x.ContentType.IsIn(ctdNames)) + .OrderBy(x => x.CreatedUtc) + .ListAsync(); - public async Task GetContentItemsAsync(ContentTypesSitemapSource source, ContentItemsQueryContext queryContext) + queryContext.ContentItems = queryResults; + + // Provide all content items with localization as reference content items. + queryContext.ReferenceContentItems = queryResults + .Where(ci => ci.Has()); + } + else if (source.LimitItems) { - var routeableContentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); + // Test that content type is still valid to include in sitemap. + var contentType = routeableContentTypeDefinitions + .FirstOrDefault(ctd => string.Equals(source.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); - if (source.IndexAll) + if (contentType == null) { - // Assumption here is that at least one content type will be localized. - var ctdNames = routeableContentTypeDefinitions.Select(ctd => ctd.Name); + return; + } + if (contentType.Parts.Any(ctd => string.Equals(ctd.Name, nameof(LocalizationPart), StringComparison.Ordinal))) + { + // Get all content items here for reference. Then reduce by default culture. + // We know that the content item should be localized. + // If it doesn't have a localization part, the content item should have been saved. var queryResults = await _session.Query() - .With(x => x.Published && x.ContentType.IsIn(ctdNames)) - .OrderBy(x => x.CreatedUtc) - .ListAsync(); + .With(ci => ci.ContentType == source.LimitedContentType.ContentTypeName && ci.Published) + .OrderBy(ci => ci.CreatedUtc) + .With() + .ListAsync(); - queryContext.ContentItems = queryResults; + // When limiting items Content item is valid if it is for the default culture. + var defaultCulture = await _localizationService.GetDefaultCultureAsync(); + + // Reduce by default culture. + var items = queryResults + .Where(ci => string.Equals(ci.As().Culture, defaultCulture, StringComparison.Ordinal)) + .Skip(source.LimitedContentType.Skip) + .Take(source.LimitedContentType.Take); + + queryContext.ContentItems = items; // Provide all content items with localization as reference content items. queryContext.ReferenceContentItems = queryResults .Where(ci => ci.Has()); } - else if (source.LimitItems) - { - // Test that content type is still valid to include in sitemap. - var contentType = routeableContentTypeDefinitions - .FirstOrDefault(ctd => string.Equals(source.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); - - if (contentType == null) - { - return; - } - - if (contentType.Parts.Any(ctd => string.Equals(ctd.Name, nameof(LocalizationPart), StringComparison.Ordinal))) - { - // Get all content items here for reference. Then reduce by default culture. - // We know that the content item should be localized. - // If it doesn't have a localization part, the content item should have been saved. - var queryResults = await _session.Query() - .With(ci => ci.ContentType == source.LimitedContentType.ContentTypeName && ci.Published) - .OrderBy(ci => ci.CreatedUtc) - .With() - .ListAsync(); - - // When limiting items Content item is valid if it is for the default culture. - var defaultCulture = await _localizationService.GetDefaultCultureAsync(); - - // Reduce by default culture. - var items = queryResults - .Where(ci => string.Equals(ci.As().Culture, defaultCulture, StringComparison.Ordinal)) - .Skip(source.LimitedContentType.Skip) - .Take(source.LimitedContentType.Take); - - queryContext.ContentItems = items; - - // Provide all content items with localization as reference content items. - queryContext.ReferenceContentItems = queryResults - .Where(ci => ci.Has()); - } - else - { - // Content type is not localized. Produce standard results. - var queryResults = await _session.Query() - .With(x => x.ContentType == source.LimitedContentType.ContentTypeName && x.Published) - .OrderBy(x => x.CreatedUtc) - .Skip(source.LimitedContentType.Skip) - .Take(source.LimitedContentType.Take) - .ListAsync(); - - queryContext.ContentItems = queryResults; - } - } else { - // Test that content types are still valid to include in sitemap. - var typesToIndex = routeableContentTypeDefinitions - .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) - .Select(x => x.Name); - - // No advantage here in reducing with localized index. + // Content type is not localized. Produce standard results. var queryResults = await _session.Query() - .With(x => x.ContentType.IsIn(typesToIndex) && x.Published) + .With(x => x.ContentType == source.LimitedContentType.ContentTypeName && x.Published) .OrderBy(x => x.CreatedUtc) + .Skip(source.LimitedContentType.Skip) + .Take(source.LimitedContentType.Take) .ListAsync(); queryContext.ContentItems = queryResults; - - // Provide all content items with localization as reference content items. - queryContext.ReferenceContentItems = queryResults - .Where(ci => ci.Has()); } } + else + { + // Test that content types are still valid to include in sitemap. + var typesToIndex = routeableContentTypeDefinitions + .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) + .Select(x => x.Name); + + // No advantage here in reducing with localized index. + var queryResults = await _session.Query() + .With(x => x.ContentType.IsIn(typesToIndex) && x.Published) + .OrderBy(x => x.CreatedUtc) + .ListAsync(); + + queryContext.ContentItems = queryResults; + + // Provide all content items with localization as reference content items. + queryContext.ReferenceContentItems = queryResults + .Where(ci => ci.Has()); + } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/SitemapUrlHrefLangExtendedMetadataProvider.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/SitemapUrlHrefLangExtendedMetadataProvider.cs index 6f04a469a5a..84c0fe49f96 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/SitemapUrlHrefLangExtendedMetadataProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Sitemaps/SitemapUrlHrefLangExtendedMetadataProvider.cs @@ -7,62 +7,61 @@ using OrchardCore.Sitemaps.Builders; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.ContentLocalization.Sitemaps +namespace OrchardCore.ContentLocalization.Sitemaps; + +public class SitemapUrlHrefLangExtendedMetadataProvider : ISitemapContentItemExtendedMetadataProvider { - public class SitemapUrlHrefLangExtendedMetadataProvider : ISitemapContentItemExtendedMetadataProvider + private static readonly XNamespace _extendedNamespace = "http://www.w3.org/1999/xhtml"; + private static readonly XAttribute _extendedAttribute = new(XNamespace.Xmlns + "xhtml", _extendedNamespace); + + private readonly IContentManager _contentManager; + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + + public SitemapUrlHrefLangExtendedMetadataProvider( + IContentManager contentManager, + IRouteableContentTypeCoordinator routeableContentTypeCoordinator + ) { - private static readonly XNamespace _extendedNamespace = "http://www.w3.org/1999/xhtml"; - private static readonly XAttribute _extendedAttribute = new(XNamespace.Xmlns + "xhtml", _extendedNamespace); + _contentManager = contentManager; + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + } - private readonly IContentManager _contentManager; - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + public XAttribute GetExtendedAttribute => _extendedAttribute; - public SitemapUrlHrefLangExtendedMetadataProvider( - IContentManager contentManager, - IRouteableContentTypeCoordinator routeableContentTypeCoordinator - ) + public async Task ApplyExtendedMetadataAsync( + SitemapBuilderContext context, + ContentItemsQueryContext queryContext, + ContentItem contentItem, + XElement url) + { + var part = contentItem.As(); + if (part == null) { - _contentManager = contentManager; - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + return true; } - public XAttribute GetExtendedAttribute => _extendedAttribute; + var localizedContentParts = queryContext.ReferenceContentItems + .Select(ci => ci.As()) + .Where(cp => cp.LocalizationSet == part.LocalizationSet); - public async Task ApplyExtendedMetadataAsync( - SitemapBuilderContext context, - ContentItemsQueryContext queryContext, - ContentItem contentItem, - XElement url) + foreach (var localizedPart in localizedContentParts) { - var part = contentItem.As(); - if (part == null) + var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(localizedPart.ContentItem); + if (sitemapMetadataAspect.Exclude) { - return true; + continue; } - var localizedContentParts = queryContext.ReferenceContentItems - .Select(ci => ci.As()) - .Where(cp => cp.LocalizationSet == part.LocalizationSet); + var hrefValue = await _routeableContentTypeCoordinator.GetRouteAsync(context, localizedPart.ContentItem); - foreach (var localizedPart in localizedContentParts) - { - var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(localizedPart.ContentItem); - if (sitemapMetadataAspect.Exclude) - { - continue; - } + var linkNode = new XElement(_extendedNamespace + "link", + new XAttribute("rel", "alternate"), + new XAttribute("hreflang", localizedPart.Culture), + new XAttribute("href", hrefValue)); - var hrefValue = await _routeableContentTypeCoordinator.GetRouteAsync(context, localizedPart.ContentItem); - - var linkNode = new XElement(_extendedNamespace + "link", - new XAttribute("rel", "alternate"), - new XAttribute("hreflang", localizedPart.Culture), - new XAttribute("href", hrefValue)); - - url.Add(linkNode); - } - - return true; + url.Add(linkNode); } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Startup.cs index 9e6172e1566..00ac9de8f7a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/Startup.cs @@ -25,70 +25,69 @@ using OrchardCore.Settings; using OrchardCore.Sitemaps.Builders; -namespace OrchardCore.ContentLocalization +namespace OrchardCore.ContentLocalization; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }) - .AddLiquidFilter("localization_set"); + o.MemberAccessStrategy.Register(); + }) + .AddLiquidFilter("localization_set"); - services.AddScoped(); - services.AddSingleton(); - services.AddContentLocalization(); + services.AddScoped(); + services.AddSingleton(); + services.AddContentLocalization(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddScoped(); - services.AddTransient(); - services.AddScoped, LocalizationContentsAdminListDisplayDriver>(); - } + services.AddScoped(); + services.AddTransient(); + services.AddScoped, LocalizationContentsAdminListDisplayDriver>(); } +} - [Feature("OrchardCore.ContentLocalization.ContentCulturePicker")] - public sealed class ContentPickerStartup : StartupBase +[Feature("OrchardCore.ContentLocalization.ContentCulturePicker")] +public sealed class ContentPickerStartup : StartupBase +{ + private readonly IShellConfiguration _shellConfiguration; + public ContentPickerStartup(IShellConfiguration shellConfiguration) { - private readonly IShellConfiguration _shellConfiguration; - public ContentPickerStartup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + _shellConfiguration = shellConfiguration; + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped, ContentCulturePickerNavbarDisplayDriver>(); - services.AddLiquidFilter("switch_culture_url"); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, ContentCulturePickerSettingsDriver>(); - services.AddScoped, ContentRequestCultureProviderSettingsDriver>(); - services.Configure(options => options.AddInitialRequestCultureProvider(new ContentRequestCultureProvider())); - services.Configure(_shellConfiguration.GetSection("OrchardCore_ContentLocalization_CulturePickerOptions")); - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped, ContentCulturePickerNavbarDisplayDriver>(); + services.AddLiquidFilter("switch_culture_url"); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, ContentCulturePickerSettingsDriver>(); + services.AddScoped, ContentRequestCultureProviderSettingsDriver>(); + services.Configure(options => options.AddInitialRequestCultureProvider(new ContentRequestCultureProvider())); + services.Configure(_shellConfiguration.GetSection("OrchardCore_ContentLocalization_CulturePickerOptions")); + } - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "RedirectToLocalizedContent", - areaName: "OrchardCore.ContentLocalization", - pattern: "RedirectToLocalizedContent", - defaults: new { controller = "ContentCulturePicker", action = "RedirectToLocalizedContent" } - ); - } + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "RedirectToLocalizedContent", + areaName: "OrchardCore.ContentLocalization", + pattern: "RedirectToLocalizedContent", + defaults: new { controller = "ContentCulturePicker", action = "RedirectToLocalizedContent" } + ); } +} - [Feature("OrchardCore.ContentLocalization.Sitemaps")] - public sealed class SitemapsStartup : StartupBase +[Feature("OrchardCore.ContentLocalization.Sitemaps")] +public sealed class SitemapsStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.Replace(ServiceDescriptor.Scoped()); - } + services.AddScoped(); + services.Replace(ServiceDescriptor.Scoped()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationContentsAdminFilterViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationContentsAdminFilterViewModel.cs index 6f5cc5684ec..6c528e6de6a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationContentsAdminFilterViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationContentsAdminFilterViewModel.cs @@ -2,14 +2,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.ContentLocalization.ViewModels +namespace OrchardCore.ContentLocalization.ViewModels; + +public class LocalizationContentsAdminFilterViewModel { - public class LocalizationContentsAdminFilterViewModel - { - public bool ShowLocalizedContentTypes { get; set; } - public string SelectedCulture { get; set; } + public bool ShowLocalizedContentTypes { get; set; } + public string SelectedCulture { get; set; } - [BindNever] - public List Cultures { get; set; } - } + [BindNever] + public List Cultures { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationPartViewModel.cs index f13d2eb5642..cc7ccaf4dd7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentLocalization/ViewModels/LocalizationPartViewModel.cs @@ -4,27 +4,26 @@ using OrchardCore.ContentLocalization.Models; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentLocalization.ViewModels +namespace OrchardCore.ContentLocalization.ViewModels; + +public class LocalizationPartViewModel : ShapeViewModel { - public class LocalizationPartViewModel : ShapeViewModel - { - public string LocalizationSet { get; set; } - public string Culture { get; set; } + public string LocalizationSet { get; set; } + public string Culture { get; set; } - [BindNever] - public CultureInfo CultureInfo => CultureInfo.GetCultureInfo(Culture); + [BindNever] + public CultureInfo CultureInfo => CultureInfo.GetCultureInfo(Culture); - [BindNever] - public LocalizationPart LocalizationPart { get; set; } + [BindNever] + public LocalizationPart LocalizationPart { get; set; } - [BindNever] - public IEnumerable ContentItemCultures { get; set; } - } + [BindNever] + public IEnumerable ContentItemCultures { get; set; } +} - public class LocalizationLinksViewModel - { - public bool IsDeleted { get; set; } - public string ContentItemId { get; set; } - public CultureInfo Culture { get; set; } - } +public class LocalizationLinksViewModel +{ + public bool IsDeleted { get; set; } + public string ContentItemId { get; set; } + public CultureInfo Culture { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Controllers/PreviewController.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Controllers/PreviewController.cs index db53aebb116..f9bed8ed91c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Controllers/PreviewController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Controllers/PreviewController.cs @@ -12,214 +12,213 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Modules; -namespace OrchardCore.ContentPreview.Controllers +namespace OrchardCore.ContentPreview.Controllers; + +public class PreviewController : Controller { - public class PreviewController : Controller + private readonly IContentManager _contentManager; + private readonly IContentManagerSession _contentManagerSession; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IAuthorizationService _authorizationService; + private readonly IClock _clock; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public PreviewController( + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + IContentManagerSession contentManagerSession, + IAuthorizationService authorizationService, + IClock clock, + IUpdateModelAccessor updateModelAccessor) { - private readonly IContentManager _contentManager; - private readonly IContentManagerSession _contentManagerSession; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IAuthorizationService _authorizationService; - private readonly IClock _clock; - private readonly IUpdateModelAccessor _updateModelAccessor; - - public PreviewController( - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - IContentManagerSession contentManagerSession, - IAuthorizationService authorizationService, - IClock clock, - IUpdateModelAccessor updateModelAccessor) - { - _authorizationService = authorizationService; - _clock = clock; - _contentItemDisplayManager = contentItemDisplayManager; - _contentManager = contentManager; - _contentManagerSession = contentManagerSession; - _updateModelAccessor = updateModelAccessor; - } + _authorizationService = authorizationService; + _clock = clock; + _contentItemDisplayManager = contentItemDisplayManager; + _contentManager = contentManager; + _contentManagerSession = contentManagerSession; + _updateModelAccessor = updateModelAccessor; + } - public IActionResult Index() - { - return View(); - } + public IActionResult Index() + { + return View(); + } - [HttpPost] - public async Task Render() + [HttpPost] + public async Task Render() + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PreviewContent)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PreviewContent)) - { - return this.ChallengeOrForbid(); - } + return this.ChallengeOrForbid(); + } - // Mark request as a `Preview` request so that drivers / handlers or underlying services can be aware of an active preview mode. - HttpContext.Features.Set(new ContentPreviewFeature()); + // Mark request as a `Preview` request so that drivers / handlers or underlying services can be aware of an active preview mode. + HttpContext.Features.Set(new ContentPreviewFeature()); - var contentItemType = Request.Form["ContentItemType"]; - var contentItem = await _contentManager.NewAsync(contentItemType); + var contentItemType = Request.Form["ContentItemType"]; + var contentItem = await _contentManager.NewAsync(contentItemType); - // Assign the ids from the currently edited item so that validation thinks - // it's working on the same item. For instance if drivers are checking name unicity - // they need to think this is the same existing item (AutoroutePart). + // Assign the ids from the currently edited item so that validation thinks + // it's working on the same item. For instance if drivers are checking name unicity + // they need to think this is the same existing item (AutoroutePart). - var contentItemId = Request.Form["PreviewContentItemId"]; - var contentItemVersionId = Request.Form["PreviewContentItemVersionId"]; + var contentItemId = Request.Form["PreviewContentItemId"]; + var contentItemVersionId = Request.Form["PreviewContentItemVersionId"]; - // Unique contentItem.Id that only Preview is using such that another - // stored document can't have the same one in the IContentManagerSession index. + // Unique contentItem.Id that only Preview is using such that another + // stored document can't have the same one in the IContentManagerSession index. - contentItem.Id = -1; - contentItem.ContentItemId = contentItemId; - contentItem.ContentItemVersionId = contentItemVersionId; - contentItem.CreatedUtc = _clock.UtcNow; - contentItem.ModifiedUtc = _clock.UtcNow; - contentItem.PublishedUtc = _clock.UtcNow; - contentItem.Published = true; + contentItem.Id = -1; + contentItem.ContentItemId = contentItemId; + contentItem.ContentItemVersionId = contentItemVersionId; + contentItem.CreatedUtc = _clock.UtcNow; + contentItem.ModifiedUtc = _clock.UtcNow; + contentItem.PublishedUtc = _clock.UtcNow; + contentItem.Published = true; - // TODO: we should probably get this value from the main editor as it might impact validators. - _ = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); + // TODO: we should probably get this value from the main editor as it might impact validators. + _ = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); - if (!ModelState.IsValid) + if (!ModelState.IsValid) + { + var errors = new List(); + foreach (var modelState in ValidationHelpers.GetModelStateList(ViewData, false)) { - var errors = new List(); - foreach (var modelState in ValidationHelpers.GetModelStateList(ViewData, false)) + for (var i = 0; i < modelState.Errors.Count; i++) { - for (var i = 0; i < modelState.Errors.Count; i++) - { - var modelError = modelState.Errors[i]; - var errorText = ValidationHelpers.GetModelErrorMessageOrDefault(modelError); - errors.Add(errorText); - } + var modelError = modelState.Errors[i]; + var errorText = ValidationHelpers.GetModelErrorMessageOrDefault(modelError); + errors.Add(errorText); } - - return this.InternalServerError(new { errors }); } - var previewAspect = await _contentManager.PopulateAspectAsync(contentItem, new PreviewAspect()); - - if (!string.IsNullOrEmpty(previewAspect.PreviewUrl)) - { - // The PreviewPart is configured, we need to set the fake content item. - _contentManagerSession.Store(contentItem); + return this.InternalServerError(new { errors }); + } - if (!previewAspect.PreviewUrl.StartsWith('/')) - { - previewAspect.PreviewUrl = "/" + previewAspect.PreviewUrl; - } + var previewAspect = await _contentManager.PopulateAspectAsync(contentItem, new PreviewAspect()); - Request.HttpContext.Items["PreviewPath"] = previewAspect.PreviewUrl; + if (!string.IsNullOrEmpty(previewAspect.PreviewUrl)) + { + // The PreviewPart is configured, we need to set the fake content item. + _contentManagerSession.Store(contentItem); - return Ok(); + if (!previewAspect.PreviewUrl.StartsWith('/')) + { + previewAspect.PreviewUrl = "/" + previewAspect.PreviewUrl; } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, "Detail"); + Request.HttpContext.Items["PreviewPath"] = previewAspect.PreviewUrl; - return View(model); + return Ok(); } + + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, "Detail"); + + return View(model); } +} - internal static class ValidationHelpers +internal static class ValidationHelpers +{ + public static string GetModelErrorMessageOrDefault(ModelError modelError) { - public static string GetModelErrorMessageOrDefault(ModelError modelError) + Debug.Assert(modelError != null); + + if (!string.IsNullOrEmpty(modelError.ErrorMessage)) { - Debug.Assert(modelError != null); + return modelError.ErrorMessage; + } - if (!string.IsNullOrEmpty(modelError.ErrorMessage)) - { - return modelError.ErrorMessage; - } + // Default in the ValidationSummary case is no error message. + return string.Empty; + } + + public static string GetModelErrorMessageOrDefault( + ModelError modelError, + ModelStateEntry containingEntry, + ModelExplorer modelExplorer) + { + Debug.Assert(modelError != null); + Debug.Assert(containingEntry != null); + Debug.Assert(modelExplorer != null); - // Default in the ValidationSummary case is no error message. - return string.Empty; + if (!string.IsNullOrEmpty(modelError.ErrorMessage)) + { + return modelError.ErrorMessage; } - public static string GetModelErrorMessageOrDefault( - ModelError modelError, - ModelStateEntry containingEntry, - ModelExplorer modelExplorer) + // Default in the ValidationMessage case is a fallback error message. + var attemptedValue = containingEntry.AttemptedValue ?? "null"; + return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue); + } + + // Returns non-null list of model states, which caller will render in order provided. + public static IList GetModelStateList( + ViewDataDictionary viewData, + bool excludePropertyErrors) + { + if (excludePropertyErrors) { - Debug.Assert(modelError != null); - Debug.Assert(containingEntry != null); - Debug.Assert(modelExplorer != null); + viewData.ModelState.TryGetValue(viewData.TemplateInfo.HtmlFieldPrefix, out var ms); - if (!string.IsNullOrEmpty(modelError.ErrorMessage)) + if (ms != null) { - return modelError.ErrorMessage; + return new[] { ms }; } - - // Default in the ValidationMessage case is a fallback error message. - var attemptedValue = containingEntry.AttemptedValue ?? "null"; - return modelExplorer.Metadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(attemptedValue); } - - // Returns non-null list of model states, which caller will render in order provided. - public static IList GetModelStateList( - ViewDataDictionary viewData, - bool excludePropertyErrors) + else if (viewData.ModelState.Count > 0) { - if (excludePropertyErrors) - { - viewData.ModelState.TryGetValue(viewData.TemplateInfo.HtmlFieldPrefix, out var ms); + var metadata = viewData.ModelMetadata; + var modelStateDictionary = viewData.ModelState; + var entries = new List(); + Visit(modelStateDictionary.Root, metadata, entries); - if (ms != null) - { - return new[] { ms }; - } - } - else if (viewData.ModelState.Count > 0) + if (entries.Count < modelStateDictionary.Count) { - var metadata = viewData.ModelMetadata; - var modelStateDictionary = viewData.ModelState; - var entries = new List(); - Visit(modelStateDictionary.Root, metadata, entries); - - if (entries.Count < modelStateDictionary.Count) + // Account for entries in the ModelStateDictionary that do not have corresponding ModelMetadata values. + foreach (var entry in modelStateDictionary) { - // Account for entries in the ModelStateDictionary that do not have corresponding ModelMetadata values. - foreach (var entry in modelStateDictionary) + if (!entries.Contains(entry.Value)) { - if (!entries.Contains(entry.Value)) - { - entries.Add(entry.Value); - } + entries.Add(entry.Value); } } - - return entries; } - return Array.Empty(); + return entries; } - private static void Visit( - ModelStateEntry modelStateEntry, - ModelMetadata metadata, - List orderedModelStateEntries) + return Array.Empty(); + } + + private static void Visit( + ModelStateEntry modelStateEntry, + ModelMetadata metadata, + List orderedModelStateEntries) + { + if (metadata.ElementMetadata != null && modelStateEntry.Children != null) { - if (metadata.ElementMetadata != null && modelStateEntry.Children != null) + foreach (var indexEntry in modelStateEntry.Children) { - foreach (var indexEntry in modelStateEntry.Children) - { - Visit(indexEntry, metadata.ElementMetadata, orderedModelStateEntries); - } + Visit(indexEntry, metadata.ElementMetadata, orderedModelStateEntries); } - else + } + else + { + for (var i = 0; i < metadata.Properties.Count; i++) { - for (var i = 0; i < metadata.Properties.Count; i++) + var propertyMetadata = metadata.Properties[i]; + var propertyModelStateEntry = modelStateEntry.GetModelStateForProperty(propertyMetadata.PropertyName); + if (propertyModelStateEntry != null) { - var propertyMetadata = metadata.Properties[i]; - var propertyModelStateEntry = modelStateEntry.GetModelStateForProperty(propertyMetadata.PropertyName); - if (propertyModelStateEntry != null) - { - Visit(propertyModelStateEntry, propertyMetadata, orderedModelStateEntries); - } + Visit(propertyModelStateEntry, propertyMetadata, orderedModelStateEntries); } } + } - if (!modelStateEntry.IsContainerNode) - { - orderedModelStateEntries.Add(modelStateEntry); - } + if (!modelStateEntry.IsContainerNode) + { + orderedModelStateEntries.Add(modelStateEntry); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Drivers/ContentPreviewDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Drivers/ContentPreviewDriver.cs index 9979562da5f..a657700b8a8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Drivers/ContentPreviewDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Drivers/ContentPreviewDriver.cs @@ -4,14 +4,13 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentPreview.Drivers +namespace OrchardCore.ContentPreview.Drivers; + +public sealed class ContentPreviewDriver : ContentDisplayDriver { - public sealed class ContentPreviewDriver : ContentDisplayDriver + public override IDisplayResult Edit(ContentItem contentItem, BuildEditorContext context) { - public override IDisplayResult Edit(ContentItem contentItem, BuildEditorContext context) - { - return Shape("ContentPreview_Button", new ContentItemViewModel(contentItem)) - .Location("Actions:after"); - } + return Shape("ContentPreview_Button", new ContentItemViewModel(contentItem)) + .Location("Actions:after"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Handlers/PreviewPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Handlers/PreviewPartHandler.cs index 2a38eecd141..bd9120eb5e1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Handlers/PreviewPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Handlers/PreviewPartHandler.cs @@ -11,55 +11,54 @@ using OrchardCore.ContentPreview.ViewModels; using OrchardCore.Liquid; -namespace OrchardCore.ContentPreview.Handlers +namespace OrchardCore.ContentPreview.Handlers; + +public class PreviewPartHandler : ContentPartHandler { - public class PreviewPartHandler : ContentPartHandler + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + + public PreviewPartHandler( + ILiquidTemplateManager liquidTemplateManager, + IContentDefinitionManager contentDefinitionManager) { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IContentDefinitionManager _contentDefinitionManager; + _liquidTemplateManager = liquidTemplateManager; + _contentDefinitionManager = contentDefinitionManager; + } - public PreviewPartHandler( - ILiquidTemplateManager liquidTemplateManager, - IContentDefinitionManager contentDefinitionManager) - { - _liquidTemplateManager = liquidTemplateManager; - _contentDefinitionManager = contentDefinitionManager; - } + /// + /// Get the pattern from the AutoroutePartSettings property for its type. + /// + private async Task GetPatternAsync(PreviewPart part) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "PreviewPart", StringComparison.Ordinal)); + var pattern = contentTypePartDefinition.GetSettings().Pattern; - /// - /// Get the pattern from the AutoroutePartSettings property for its type. - /// - private async Task GetPatternAsync(PreviewPart part) - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "PreviewPart", StringComparison.Ordinal)); - var pattern = contentTypePartDefinition.GetSettings().Pattern; + return pattern; + } - return pattern; - } + public override async Task GetContentItemAspectAsync(ContentItemAspectContext context, PreviewPart part) + { + var pattern = await GetPatternAsync(part); - public override async Task GetContentItemAspectAsync(ContentItemAspectContext context, PreviewPart part) + if (!string.IsNullOrEmpty(pattern)) { - var pattern = await GetPatternAsync(part); - - if (!string.IsNullOrEmpty(pattern)) + await context.ForAsync(async previewAspect => { - await context.ForAsync(async previewAspect => + var model = new PreviewPartViewModel() { - var model = new PreviewPartViewModel() - { - PreviewPart = part, - ContentItem = part.ContentItem, - }; - - previewAspect.PreviewUrl = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + PreviewPart = part, + ContentItem = part.ContentItem, + }; - previewAspect.PreviewUrl = previewAspect.PreviewUrl.Replace("\r", string.Empty).Replace("\n", string.Empty); - }); - } + previewAspect.PreviewUrl = await _liquidTemplateManager.RenderStringAsync(pattern, NullEncoder.Default, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - return; + previewAspect.PreviewUrl = previewAspect.PreviewUrl.Replace("\r", string.Empty).Replace("\n", string.Empty); + }); } + + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Migrations.cs index bbee305659d..c8cc7066a91 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Migrations.cs @@ -3,24 +3,23 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.ContentPreview +namespace OrchardCore.ContentPreview; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("PreviewPart", builder => builder - .Attachable() - .WithDescription("Provides a way to define the url that is used to render your content item for preview. You only need to use this for the content preview feature when running the frontend decoupled from the admin.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("PreviewPart", builder => builder + .Attachable() + .WithDescription("Provides a way to define the url that is used to render your content item for preview. You only need to use this for the content preview feature when running the frontend decoupled from the admin.")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPart.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPart.cs index 12bebdbf993..44edb665638 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPart.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ContentPreview.Models +namespace OrchardCore.ContentPreview.Models; + +/// +/// When attached to a content type, provides a way to define the +/// url that is used to render the content item. +/// +public class PreviewPart : ContentPart { - /// - /// When attached to a content type, provides a way to define the - /// url that is used to render the content item. - /// - public class PreviewPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPartSettings.cs index c9c0b49943f..3b7152caab9 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Models/PreviewPartSettings.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.ContentPreview.Models +namespace OrchardCore.ContentPreview.Models; + +public class PreviewPartSettings { - public class PreviewPartSettings - { - /// - /// The pattern used to build the Path. - /// - public string Pattern { get; set; } - } + /// + /// The pattern used to build the Path. + /// + public string Pattern { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/PreviewStartupFilter.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/PreviewStartupFilter.cs index 7bf5d0d989d..964d862376e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/PreviewStartupFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/PreviewStartupFilter.cs @@ -3,50 +3,49 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -namespace OrchardCore.ContentPreview +namespace OrchardCore.ContentPreview; + +public sealed class PreviewStartupFilter : IStartupFilter { - public sealed class PreviewStartupFilter : IStartupFilter + public Action Configure(Action next) { - public Action Configure(Action next) + return app => { - return app => + app.Use(async (context, next) => { - app.Use(async (context, next) => + await next(); + + if (!context.Items.TryGetValue("PreviewPath", out var previewPathObject) || previewPathObject == null) { - await next(); + return; + } - if (!context.Items.TryGetValue("PreviewPath", out var previewPathObject) || previewPathObject == null) - { - return; - } + var previewPath = previewPathObject.ToString(); - var previewPath = previewPathObject.ToString(); + if (!string.IsNullOrWhiteSpace(previewPath) && previewPath.StartsWith('/')) + { + var originalPath = context.Request.Path; + var originalQueryString = context.Request.QueryString; - if (!string.IsNullOrWhiteSpace(previewPath) && previewPath.StartsWith('/')) + context.Request.Path = previewPath; + context.Items.Remove("PreviewPath"); + + context.SetEndpoint(endpoint: null); + context.Request.RouteValues.Clear(); + + try + { + await next(); + } + finally { - var originalPath = context.Request.Path; - var originalQueryString = context.Request.QueryString; - - context.Request.Path = previewPath; - context.Items.Remove("PreviewPath"); - - context.SetEndpoint(endpoint: null); - context.Request.RouteValues.Clear(); - - try - { - await next(); - } - finally - { - context.Request.QueryString = originalQueryString; - context.Request.Path = originalPath; - } + context.Request.QueryString = originalQueryString; + context.Request.Path = originalPath; } - }); + } + }); - next(app); - }; - } + next(app); + }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ResourceManagementOptionsConfiguration.cs index 6cf778424b4..09ca7824d57 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ResourceManagementOptionsConfiguration.cs @@ -1,26 +1,25 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.ContentPreview +namespace OrchardCore.ContentPreview; + +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions - { - private static readonly ResourceManifest _manifest; + private static readonly ResourceManifest _manifest; - static ResourceManagementOptionsConfiguration() - { - _manifest = new ResourceManifest(); + static ResourceManagementOptionsConfiguration() + { + _manifest = new ResourceManifest(); - _manifest - .DefineScript("contentpreview-edit") - .SetUrl("~/OrchardCore.ContentPreview/Scripts/contentpreview.edit.min.js", "~/OrchardCore.ContentPreview/Scripts/contentpreview.edit.js") - .SetDependencies("jQuery") - .SetVersion("1.0.0"); - } + _manifest + .DefineScript("contentpreview-edit") + .SetUrl("~/OrchardCore.ContentPreview/Scripts/contentpreview.edit.min.js", "~/OrchardCore.ContentPreview/Scripts/contentpreview.edit.js") + .SetDependencies("jQuery") + .SetVersion("1.0.0"); + } - public void Configure(ResourceManagementOptions options) - { - options.ResourceManifests.Add(_manifest); - } + public void Configure(ResourceManagementOptions options) + { + options.ResourceManifests.Add(_manifest); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Settings/PreviewPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Settings/PreviewPartSettingsDisplayDriver.cs index 694ec12a3d2..3ad668af5b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Settings/PreviewPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Settings/PreviewPartSettingsDisplayDriver.cs @@ -8,53 +8,52 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Liquid; -namespace OrchardCore.ContentPreview.Settings +namespace OrchardCore.ContentPreview.Settings; + +public sealed class PreviewPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class PreviewPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly ILiquidTemplateManager _templateManager; + private readonly ILiquidTemplateManager _templateManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public PreviewPartSettingsDisplayDriver( - ILiquidTemplateManager templateManager, - IStringLocalizer localizer) - { - _templateManager = templateManager; - S = localizer; - } + public PreviewPartSettingsDisplayDriver( + ILiquidTemplateManager templateManager, + IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("PreviewPartSettings_Edit", model => { - return Initialize("PreviewPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.Pattern = settings.Pattern; - model.PreviewPartSettings = settings; - }).Location("Content"); - } + model.Pattern = settings.Pattern; + model.PreviewPartSettings = settings; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new PreviewPartSettingsViewModel(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new PreviewPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Pattern); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Pattern); - if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) - { - context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else + if (!string.IsNullOrEmpty(model.Pattern) && !_templateManager.Validate(model.Pattern, out var errors)) + { + context.Updater.ModelState.AddModelError(nameof(model.Pattern), S["Pattern doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + else + { + context.Builder.WithSettings(new PreviewPartSettings { - context.Builder.WithSettings(new PreviewPartSettings - { - Pattern = model.Pattern - }); - } - - return Edit(contentTypePartDefinition, context); + Pattern = model.Pattern + }); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Startup.cs index bfc91840a7a..7f4052e5c2b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/Startup.cs @@ -12,23 +12,22 @@ using OrchardCore.Data.Migration; using OrchardCore.ResourceManagement; -namespace OrchardCore.ContentPreview +namespace OrchardCore.ContentPreview; + +public sealed class Startup : Modules.StartupBase { - public sealed class Startup : Modules.StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient, ResourceManagementOptionsConfiguration>(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); - services.AddScoped(); + services.AddScoped(); - // Preview Part - services.AddContentPart() - .AddHandler(); + // Preview Part + services.AddContentPart() + .AddHandler(); - services.AddDataMigration(); - services.AddScoped(); - services.TryAddEnumerable(ServiceDescriptor.Singleton()); - } + services.AddDataMigration(); + services.AddScoped(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartSettingsViewModel.cs index 834f3d12dd7..7369733172e 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartSettingsViewModel.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentPreview.Models; -namespace OrchardCore.ContentPreview.ViewModels +namespace OrchardCore.ContentPreview.ViewModels; + +public class PreviewPartSettingsViewModel { - public class PreviewPartSettingsViewModel - { - public string Pattern { get; set; } - public PreviewPartSettings PreviewPartSettings { get; set; } - } + public string Pattern { get; set; } + public PreviewPartSettings PreviewPartSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartViewModel.cs index 1e97709ac8b..ff4ef6e5775 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentPreview/ViewModels/PreviewPartViewModel.cs @@ -2,16 +2,15 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentPreview.Models; -namespace OrchardCore.ContentPreview.ViewModels +namespace OrchardCore.ContentPreview.ViewModels; + +public class PreviewPartViewModel { - public class PreviewPartViewModel - { - public string Pattern { get; set; } + public string Pattern { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public PreviewPart PreviewPart { get; set; } - } + [BindNever] + public PreviewPart PreviewPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/AdminMenu.cs index bbdba4ac80f..76fd2db6ee3 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/AdminMenu.cs @@ -4,43 +4,42 @@ using OrchardCore.Mvc.Core.Utilities; using OrchardCore.Navigation; -namespace OrchardCore.ContentTypes +namespace OrchardCore.ContentTypes; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider - { - private static readonly string _adminControllerName = typeof(AdminController).ControllerName(); + private static readonly string _adminControllerName = typeof(AdminController).ControllerName(); - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Content"], content => content - .Add(S["Content Definition"], S["Content Definition"].PrefixPosition("9"), contentDefinition => contentDefinition - .Add(S["Content Types"], S["Content Types"].PrefixPosition("1"), contentTypes => contentTypes - .Action(nameof(AdminController.List), _adminControllerName, "OrchardCore.ContentTypes") - .Permission(Permissions.ViewContentTypes) - .LocalNav() - ) - .Add(S["Content Parts"], S["Content Parts"].PrefixPosition("2"), contentParts => contentParts - .Action(nameof(AdminController.ListParts), _adminControllerName, "OrchardCore.ContentTypes") - .Permission(Permissions.ViewContentTypes) - .LocalNav() - ) + builder + .Add(S["Content"], content => content + .Add(S["Content Definition"], S["Content Definition"].PrefixPosition("9"), contentDefinition => contentDefinition + .Add(S["Content Types"], S["Content Types"].PrefixPosition("1"), contentTypes => contentTypes + .Action(nameof(AdminController.List), _adminControllerName, "OrchardCore.ContentTypes") + .Permission(Permissions.ViewContentTypes) + .LocalNav() ) - ); + .Add(S["Content Parts"], S["Content Parts"].PrefixPosition("2"), contentParts => contentParts + .Action(nameof(AdminController.ListParts), _adminControllerName, "OrchardCore.ContentTypes") + .Permission(Permissions.ViewContentTypes) + .LocalNav() + ) + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Controllers/AdminController.cs index a76bc76d15d..d86d4ac5691 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Controllers/AdminController.cs @@ -19,977 +19,976 @@ using OrchardCore.DisplayManagement.Notify; using OrchardCore.Routing; -namespace OrchardCore.ContentTypes.Controllers +namespace OrchardCore.ContentTypes.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IContentDefinitionService _contentDefinitionService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDocumentStore _documentStore; + private readonly IContentDefinitionDisplayManager _contentDefinitionDisplayManager; + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + private readonly INotifier _notifier; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public AdminController( + IContentDefinitionDisplayManager contentDefinitionDisplayManager, + IContentDefinitionService contentDefinitionService, + IContentDefinitionManager contentDefinitionManager, + IAuthorizationService authorizationService, + IDocumentStore documentStore, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + INotifier notifier, + IUpdateModelAccessor updateModelAccessor) { - private readonly IContentDefinitionService _contentDefinitionService; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDocumentStore _documentStore; - private readonly IContentDefinitionDisplayManager _contentDefinitionDisplayManager; - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - private readonly INotifier _notifier; - private readonly IUpdateModelAccessor _updateModelAccessor; - - public AdminController( - IContentDefinitionDisplayManager contentDefinitionDisplayManager, - IContentDefinitionService contentDefinitionService, - IContentDefinitionManager contentDefinitionManager, - IAuthorizationService authorizationService, - IDocumentStore documentStore, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer, - INotifier notifier, - IUpdateModelAccessor updateModelAccessor) - { - _notifier = notifier; - _contentDefinitionDisplayManager = contentDefinitionDisplayManager; - _documentStore = documentStore; - _authorizationService = authorizationService; - _contentDefinitionService = contentDefinitionService; - _contentDefinitionManager = contentDefinitionManager; - _updateModelAccessor = updateModelAccessor; - - H = htmlLocalizer; - S = stringLocalizer; - } - - public Task Index() - { - return List(); - } - - #region Types - - [Admin("ContentTypes/List", "ListContentTypes")] - public async Task List() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewContentTypes)) - { - return Forbid(); - } + _notifier = notifier; + _contentDefinitionDisplayManager = contentDefinitionDisplayManager; + _documentStore = documentStore; + _authorizationService = authorizationService; + _contentDefinitionService = contentDefinitionService; + _contentDefinitionManager = contentDefinitionManager; + _updateModelAccessor = updateModelAccessor; + + H = htmlLocalizer; + S = stringLocalizer; + } - return View("List", new ListContentTypesViewModel - { - Types = await _contentDefinitionService.GetTypesAsync() - }); + public Task Index() + { + return List(); + } + + #region Types + + [Admin("ContentTypes/List", "ListContentTypes")] + public async Task List() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewContentTypes)) + { + return Forbid(); } - [Admin("ContentTypes/Create", "CreateType")] - public async Task Create(string suggestion) + return View("List", new ListContentTypesViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + Types = await _contentDefinitionService.GetTypesAsync() + }); + } - return View(new CreateTypeViewModel { DisplayName = suggestion, Name = suggestion.ToSafeName() }); + [Admin("ContentTypes/Create", "CreateType")] + public async Task Create(string suggestion) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [HttpPost, ActionName("Create")] - public async Task CreatePOST(CreateTypeViewModel viewModel) + return View(new CreateTypeViewModel { DisplayName = suggestion, Name = suggestion.ToSafeName() }); + } + + [HttpPost, ActionName("Create")] + public async Task CreatePOST(CreateTypeViewModel viewModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return Forbid(); + } - viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - viewModel.Name ??= string.Empty; + viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; + viewModel.Name ??= string.Empty; - if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) - { - ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); - } - var types = await _contentDefinitionService.LoadTypesAsync(); + if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) + { + ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); + } + var types = await _contentDefinitionService.LoadTypesAsync(); - if (types.Any(t => string.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("DisplayName", S["A type with the same Display Name already exists."]); - } + if (types.Any(t => string.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("DisplayName", S["A type with the same Display Name already exists."]); + } - if (string.IsNullOrWhiteSpace(viewModel.Name)) - { - ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); - } + if (string.IsNullOrWhiteSpace(viewModel.Name)) + { + ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); + } - if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) - { - ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); - } + if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) + { + ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); + } - if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); - } + if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); + } - if (viewModel.Name.IsReservedContentName()) - { - ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); - } + if (viewModel.Name.IsReservedContentName()) + { + ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); + } - if (types.Any(t => string.Equals(t.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("Name", S["A type with the same Technical Name already exists."]); - } + if (types.Any(t => string.Equals(t.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("Name", S["A type with the same Technical Name already exists."]); + } - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); - return View(viewModel); - } + if (!ModelState.IsValid) + { + await _documentStore.CancelAsync(); + return View(viewModel); + } - var contentTypeDefinition = await _contentDefinitionService.AddTypeAsync(viewModel.Name, viewModel.DisplayName); + var contentTypeDefinition = await _contentDefinitionService.AddTypeAsync(viewModel.Name, viewModel.DisplayName); - var typeViewModel = new EditTypeViewModel(contentTypeDefinition); + var typeViewModel = new EditTypeViewModel(contentTypeDefinition); - await _notifier.SuccessAsync(H["The \"{0}\" content type has been created.", typeViewModel.DisplayName]); + await _notifier.SuccessAsync(H["The \"{0}\" content type has been created.", typeViewModel.DisplayName]); - return RedirectToAction("AddPartsTo", new { id = typeViewModel.Name }); + return RedirectToAction("AddPartsTo", new { id = typeViewModel.Name }); + } + + [Admin("ContentTypes/Edit/{id}", "EditType")] + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [Admin("ContentTypes/Edit/{id}", "EditType")] - public async Task Edit(string id) + var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); + + if (typeViewModel == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return NotFound(); + } - var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); + typeViewModel.Editor = await _contentDefinitionDisplayManager.BuildTypeEditorAsync(typeViewModel.TypeDefinition, _updateModelAccessor.ModelUpdater); - if (typeViewModel == null) - { - return NotFound(); - } + return View(typeViewModel); + } + + [HttpPost, ActionName("Edit")] + [FormValueRequired("submit.Save")] + public async Task EditPOST(string id, EditTypeViewModel viewModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - typeViewModel.Editor = await _contentDefinitionDisplayManager.BuildTypeEditorAsync(typeViewModel.TypeDefinition, _updateModelAccessor.ModelUpdater); + var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(id); - return View(typeViewModel); + if (contentTypeDefinition == null) + { + return NotFound(); } - [HttpPost, ActionName("Edit")] - [FormValueRequired("submit.Save")] - public async Task EditPOST(string id, EditTypeViewModel viewModel) + viewModel.Settings = contentTypeDefinition.Settings; + viewModel.TypeDefinition = contentTypeDefinition; + viewModel.DisplayName = contentTypeDefinition.DisplayName; + viewModel.Editor = await _contentDefinitionDisplayManager.UpdateTypeEditorAsync(contentTypeDefinition, _updateModelAccessor.ModelUpdater); + + if (!ModelState.IsValid) + { + await _documentStore.CancelAsync(); + + return View(viewModel); + } + else { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + var ownedPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(contentTypeDefinition.Name); + if (ownedPartDefinition != null && viewModel.OrderedFieldNames != null) { - return Forbid(); + await _contentDefinitionService.AlterPartFieldsOrderAsync(ownedPartDefinition, viewModel.OrderedFieldNames); } + await _contentDefinitionService.AlterTypePartsOrderAsync(contentTypeDefinition, viewModel.OrderedPartNames); + await _notifier.SuccessAsync(H["\"{0}\" settings have been saved.", contentTypeDefinition.Name]); + } - var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(id); + return RedirectToAction(nameof(Edit), new { id }); + } - if (contentTypeDefinition == null) - { - return NotFound(); - } + [HttpPost, ActionName("Edit")] + [FormValueRequired("submit.Delete")] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - viewModel.Settings = contentTypeDefinition.Settings; - viewModel.TypeDefinition = contentTypeDefinition; - viewModel.DisplayName = contentTypeDefinition.DisplayName; - viewModel.Editor = await _contentDefinitionDisplayManager.UpdateTypeEditorAsync(contentTypeDefinition, _updateModelAccessor.ModelUpdater); + var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); + if (typeViewModel == null) + { + return NotFound(); + } - return View(viewModel); - } - else - { - var ownedPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(contentTypeDefinition.Name); - if (ownedPartDefinition != null && viewModel.OrderedFieldNames != null) - { - await _contentDefinitionService.AlterPartFieldsOrderAsync(ownedPartDefinition, viewModel.OrderedFieldNames); - } - await _contentDefinitionService.AlterTypePartsOrderAsync(contentTypeDefinition, viewModel.OrderedPartNames); - await _notifier.SuccessAsync(H["\"{0}\" settings have been saved.", contentTypeDefinition.Name]); - } + await _contentDefinitionService.RemoveTypeAsync(id, true); - return RedirectToAction(nameof(Edit), new { id }); + await _notifier.SuccessAsync(H["\"{0}\" has been removed.", typeViewModel.DisplayName]); + + return RedirectToAction(nameof(List)); + } + + [Admin("ContentTypes/AddPartsTo/{id}", "AddPartsTo")] + public async Task AddPartsTo(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [HttpPost, ActionName("Edit")] - [FormValueRequired("submit.Delete")] - public async Task Delete(string id) + var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); + + if (typeViewModel == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return NotFound(); + } - var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); + var typePartNames = new HashSet(typeViewModel.TypeDefinition.Parts.Select(p => p.IsNamedPart() ? p.Name : p.PartDefinition.Name)); - if (typeViewModel == null) - { - return NotFound(); - } + var viewModel = new AddPartsViewModel + { + Type = typeViewModel, + PartSelections = (await _contentDefinitionService.GetPartsAsync(metadataPartsOnly: false)) + .Where(cpd => !typePartNames.Contains(cpd.Name, StringComparer.OrdinalIgnoreCase) && cpd.PartDefinition != null && cpd.PartDefinition.GetSettings().Attachable) + .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description }) + .ToList() + }; + + return View(viewModel); + } - await _contentDefinitionService.RemoveTypeAsync(id, true); + [Admin("ContentTypes/AddReusablePartTo/{id}", "AddReusablePartTo")] + public async Task AddReusablePartTo(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - await _notifier.SuccessAsync(H["\"{0}\" has been removed.", typeViewModel.DisplayName]); + var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); - return RedirectToAction(nameof(List)); + if (typeViewModel == null) + { + return NotFound(); } - [Admin("ContentTypes/AddPartsTo/{id}", "AddPartsTo")] - public async Task AddPartsTo(string id) + var reusableParts = (await _contentDefinitionService.GetPartsAsync(metadataPartsOnly: false)) + .Where(cpd => cpd.PartDefinition != null && + cpd.PartDefinition.GetSettings().Attachable && + cpd.PartDefinition.GetSettings().Reusable); + + var viewModel = new AddReusablePartViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + Type = typeViewModel, + PartSelections = reusableParts + .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description }) + .ToList(), + SelectedPartName = reusableParts.FirstOrDefault()?.Name + }; + + return View(viewModel); + } - var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); + [HttpPost, ActionName("AddPartsTo")] + public async Task AddPartsToPOST(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (typeViewModel == null) - { - return NotFound(); - } + var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); - var typePartNames = new HashSet(typeViewModel.TypeDefinition.Parts.Select(p => p.IsNamedPart() ? p.Name : p.PartDefinition.Name)); + if (typeViewModel == null) + { + return NotFound(); + } - var viewModel = new AddPartsViewModel - { - Type = typeViewModel, - PartSelections = (await _contentDefinitionService.GetPartsAsync(metadataPartsOnly: false)) - .Where(cpd => !typePartNames.Contains(cpd.Name, StringComparer.OrdinalIgnoreCase) && cpd.PartDefinition != null && cpd.PartDefinition.GetSettings().Attachable) - .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description }) - .ToList() - }; + var viewModel = new AddPartsViewModel(); + if (!await TryUpdateModelAsync(viewModel)) + { + return await AddPartsTo(id); + } - return View(viewModel); + var partsToAdd = viewModel.PartSelections.Where(ps => ps.IsSelected).Select(ps => ps.PartName); + foreach (var partToAdd in partsToAdd) + { + await _contentDefinitionService.AddPartToTypeAsync(partToAdd, typeViewModel.Name); + await _notifier.SuccessAsync(H["The \"{0}\" part has been added.", partToAdd]); } - [Admin("ContentTypes/AddReusablePartTo/{id}", "AddReusablePartTo")] - public async Task AddReusablePartTo(string id) + if (!ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + await _documentStore.CancelAsync(); + return await AddPartsTo(id); + } - var typeViewModel = await _contentDefinitionService.GetTypeAsync(id); + return RedirectToAction(nameof(Edit), new { id }); + } - if (typeViewModel == null) - { - return NotFound(); - } + [HttpPost, ActionName("AddReusablePartTo")] + public async Task AddReusablePartToPOST(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - var reusableParts = (await _contentDefinitionService.GetPartsAsync(metadataPartsOnly: false)) - .Where(cpd => cpd.PartDefinition != null && - cpd.PartDefinition.GetSettings().Attachable && - cpd.PartDefinition.GetSettings().Reusable); + var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); - var viewModel = new AddReusablePartViewModel - { - Type = typeViewModel, - PartSelections = reusableParts - .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description }) - .ToList(), - SelectedPartName = reusableParts.FirstOrDefault()?.Name - }; + if (typeViewModel == null) + { + return NotFound(); + } - return View(viewModel); + var viewModel = new AddReusablePartViewModel(); + if (!await TryUpdateModelAsync(viewModel)) + { + return await AddReusablePartTo(id); } - [HttpPost, ActionName("AddPartsTo")] - public async Task AddPartsToPOST(string id) + viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; + viewModel.Name ??= string.Empty; + + if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); + } - var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); + if (typeViewModel.TypeDefinition.Parts.Any(f => string.Equals(f.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("DisplayName", S["A part with the same Display Name already exists."]); + } - if (typeViewModel == null) - { - return NotFound(); - } + if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) + { + ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); + } - var viewModel = new AddPartsViewModel(); - if (!await TryUpdateModelAsync(viewModel)) - { - return await AddPartsTo(id); - } + if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); + } - var partsToAdd = viewModel.PartSelections.Where(ps => ps.IsSelected).Select(ps => ps.PartName); - foreach (var partToAdd in partsToAdd) - { - await _contentDefinitionService.AddPartToTypeAsync(partToAdd, typeViewModel.Name); - await _notifier.SuccessAsync(H["The \"{0}\" part has been added.", partToAdd]); - } + if (viewModel.Name.IsReservedContentName()) + { + ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); + } - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); - return await AddPartsTo(id); - } + if (string.IsNullOrWhiteSpace(viewModel.Name)) + { + ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); + } - return RedirectToAction(nameof(Edit), new { id }); + if (typeViewModel.TypeDefinition.Parts.Any(f => string.Equals(f.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("Name", S["A part with the same Technical Name already exists."]); } - [HttpPost, ActionName("AddReusablePartTo")] - public async Task AddReusablePartToPOST(string id) + if (!ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + await _documentStore.CancelAsync(); + return await AddReusablePartTo(id); + } - var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); + var partToAdd = viewModel.SelectedPartName; - if (typeViewModel == null) - { - return NotFound(); - } + await _contentDefinitionService.AddReusablePartToTypeAsync(viewModel.Name, viewModel.DisplayName, viewModel.Description, partToAdd, typeViewModel.Name); - var viewModel = new AddReusablePartViewModel(); - if (!await TryUpdateModelAsync(viewModel)) - { - return await AddReusablePartTo(id); - } + await _notifier.SuccessAsync(H["The \"{0}\" part has been added.", partToAdd]); - viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - viewModel.Name ??= string.Empty; + return RedirectToAction(nameof(Edit), new { id }); + } - if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) - { - ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); - } + [HttpPost, ActionName("RemovePart")] + [Admin("ContentTypes/{id}/ContentParts/{name}/Remove", "RemovePart")] + public async Task RemovePart(string id, string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (typeViewModel.TypeDefinition.Parts.Any(f => string.Equals(f.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("DisplayName", S["A part with the same Display Name already exists."]); - } + var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); - if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) - { - ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); - } + if (typeViewModel == null || !typeViewModel.TypeDefinition.Parts.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))) + { + return NotFound(); + } - if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); - } + await _contentDefinitionService.RemovePartFromTypeAsync(name, id); - if (viewModel.Name.IsReservedContentName()) - { - ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); - } + await _notifier.SuccessAsync(H["The \"{0}\" part has been removed.", name]); - if (string.IsNullOrWhiteSpace(viewModel.Name)) - { - ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); - } + return RedirectToAction(nameof(Edit), new { id }); + } - if (typeViewModel.TypeDefinition.Parts.Any(f => string.Equals(f.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("Name", S["A part with the same Technical Name already exists."]); - } + #endregion Types - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); - return await AddReusablePartTo(id); - } + #region Parts - var partToAdd = viewModel.SelectedPartName; + [Admin("ContentTypes/ListParts", "ListContentParts")] + public async Task ListParts() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewContentTypes)) + { + return Forbid(); + } - await _contentDefinitionService.AddReusablePartToTypeAsync(viewModel.Name, viewModel.DisplayName, viewModel.Description, partToAdd, typeViewModel.Name); + return View(new ListContentPartsViewModel + { + // only user-defined parts (not code as they are not configurable) + Parts = await _contentDefinitionService.GetPartsAsync(true/*metadataPartsOnly*/) + }); + } - await _notifier.SuccessAsync(H["The \"{0}\" part has been added.", partToAdd]); + [Admin("ContentParts/Create", "CreatePart")] + public async Task CreatePart(string suggestion) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - return RedirectToAction(nameof(Edit), new { id }); + return View(new CreatePartViewModel { Name = suggestion.ToSafeName() }); + } + + [HttpPost, ActionName("CreatePart")] + public async Task CreatePartPOST(CreatePartViewModel viewModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [HttpPost, ActionName("RemovePart")] - [Admin("ContentTypes/{id}/ContentParts/{name}/Remove", "RemovePart")] - public async Task RemovePart(string id, string name) + viewModel.Name ??= string.Empty; + + if (string.IsNullOrWhiteSpace(viewModel.Name)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); + } - var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); + if ((await _contentDefinitionService.LoadPartsAsync(false)).Any(p => string.Equals(p.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("Name", S["A part with the same Technical Name already exists."]); + } - if (typeViewModel == null || !typeViewModel.TypeDefinition.Parts.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase))) - { - return NotFound(); - } + if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) + { + ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); + } - await _contentDefinitionService.RemovePartFromTypeAsync(name, id); + if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); + } - await _notifier.SuccessAsync(H["The \"{0}\" part has been removed.", name]); + if (viewModel.Name.IsReservedContentName()) + { + ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); + } - return RedirectToAction(nameof(Edit), new { id }); + if (!ModelState.IsValid) + { + return View(viewModel); + } + + var partViewModel = await _contentDefinitionService.AddPartAsync(viewModel); + + if (partViewModel == null) + { + await _notifier.InformationAsync(H["The content part could not be created."]); + return View(viewModel); } - #endregion Types + await _notifier.SuccessAsync(H["The \"{0}\" content part has been created.", partViewModel.Name]); - #region Parts + return RedirectToAction(nameof(EditPart), new { id = partViewModel.Name }); + } - [Admin("ContentTypes/ListParts", "ListContentParts")] - public async Task ListParts() + [Admin("ContentParts/Edit/{id}", "EditPart")] + public async Task EditPart(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewContentTypes)) - { - return Forbid(); - } + return Forbid(); + } - return View(new ListContentPartsViewModel - { - // only user-defined parts (not code as they are not configurable) - Parts = await _contentDefinitionService.GetPartsAsync(true/*metadataPartsOnly*/) - }); + var contentPartDefinition = await _contentDefinitionManager.GetPartDefinitionAsync(id); + + if (contentPartDefinition == null) + { + return NotFound(); } - [Admin("ContentParts/Create", "CreatePart")] - public async Task CreatePart(string suggestion) + var viewModel = new EditPartViewModel(contentPartDefinition) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + Editor = await _contentDefinitionDisplayManager.BuildPartEditorAsync(contentPartDefinition, _updateModelAccessor.ModelUpdater), + }; + + return View(viewModel); + } - return View(new CreatePartViewModel { Name = suggestion.ToSafeName() }); + [HttpPost, ActionName("EditPart")] + [FormValueRequired("submit.Save")] + public async Task EditPartPOST(string id, string[] orderedFieldNames) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [HttpPost, ActionName("CreatePart")] - public async Task CreatePartPOST(CreatePartViewModel viewModel) + var contentPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(id); + + if (contentPartDefinition == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return NotFound(); + } - viewModel.Name ??= string.Empty; + var viewModel = new EditPartViewModel(contentPartDefinition) + { + Editor = await _contentDefinitionDisplayManager.UpdatePartEditorAsync(contentPartDefinition, _updateModelAccessor.ModelUpdater), + }; - if (string.IsNullOrWhiteSpace(viewModel.Name)) - { - ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); - } + if (!ModelState.IsValid) + { + await _documentStore.CancelAsync(); + return View(viewModel); + } + else + { + await _contentDefinitionService.AlterPartFieldsOrderAsync(contentPartDefinition, orderedFieldNames); + await _notifier.SuccessAsync(H["The settings of \"{0}\" have been saved.", contentPartDefinition.Name]); + } - if ((await _contentDefinitionService.LoadPartsAsync(false)).Any(p => string.Equals(p.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("Name", S["A part with the same Technical Name already exists."]); - } + return RedirectToAction(nameof(EditPart), new { id }); + } - if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) - { - ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); - } + [HttpPost, ActionName("EditPart")] + [FormValueRequired("submit.Delete")] + public async Task DeletePart(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); - } + var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - if (viewModel.Name.IsReservedContentName()) - { - ModelState.AddModelError("Name", S["The Technical Name is reserved for internal use."]); - } + if (partViewModel == null) + { + return NotFound(); + } - if (!ModelState.IsValid) - { - return View(viewModel); - } + await _contentDefinitionService.RemovePartAsync(id); + + await _notifier.InformationAsync(H["\"{0}\" has been removed.", partViewModel.DisplayName]); + + return RedirectToAction(nameof(ListParts)); + } + + [Admin("ContentTypes/AddFieldsTo/{id}", "AddFieldsTo")] + public async Task AddFieldTo(string id, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - var partViewModel = await _contentDefinitionService.AddPartAsync(viewModel); + var fields = (await _contentDefinitionService.GetFieldsAsync()).ToList(); - if (partViewModel == null) - { - await _notifier.InformationAsync(H["The content part could not be created."]); - return View(viewModel); - } + if (fields.Count == 0) + { + await _notifier.WarningAsync(H["There are no fields."]); + + return RedirectToAction(nameof(List)); + } - await _notifier.SuccessAsync(H["The \"{0}\" content part has been created.", partViewModel.Name]); + var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - return RedirectToAction(nameof(EditPart), new { id = partViewModel.Name }); + if (partViewModel == null) + { + return NotFound(); } - [Admin("ContentParts/Edit/{id}", "EditPart")] - public async Task EditPart(string id) + var viewModel = new AddFieldViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + Part = partViewModel.PartDefinition, + Fields = fields.Select(field => field.Name).OrderBy(name => name).ToList() + }; - var contentPartDefinition = await _contentDefinitionManager.GetPartDefinitionAsync(id); + ViewData["ReturnUrl"] = returnUrl; + return View(viewModel); + } - if (contentPartDefinition == null) - { - return NotFound(); - } + [HttpPost, ActionName("AddFieldTo")] + public async Task AddFieldToPOST(AddFieldViewModel viewModel, string id, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - var viewModel = new EditPartViewModel(contentPartDefinition) - { - Editor = await _contentDefinitionDisplayManager.BuildPartEditorAsync(contentPartDefinition, _updateModelAccessor.ModelUpdater), - }; + var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - return View(viewModel); + if (partViewModel == null) + { + return NotFound(); } - [HttpPost, ActionName("EditPart")] - [FormValueRequired("submit.Save")] - public async Task EditPartPOST(string id, string[] orderedFieldNames) + var fields = (await _contentDefinitionService.GetFieldsAsync()).ToList(); + + if (!fields.Any(field => string.Equals(field.Name, viewModel.FieldTypeName, StringComparison.OrdinalIgnoreCase))) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return NotFound(); + } - var contentPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(id); + var partDefinition = partViewModel.PartDefinition; - if (contentPartDefinition == null) - { - return NotFound(); - } + viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; + viewModel.Name ??= string.Empty; - var viewModel = new EditPartViewModel(contentPartDefinition) - { - Editor = await _contentDefinitionDisplayManager.UpdatePartEditorAsync(contentPartDefinition, _updateModelAccessor.ModelUpdater), - }; + if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) + { + ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); + } - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); - return View(viewModel); - } - else - { - await _contentDefinitionService.AlterPartFieldsOrderAsync(contentPartDefinition, orderedFieldNames); - await _notifier.SuccessAsync(H["The settings of \"{0}\" have been saved.", contentPartDefinition.Name]); - } + if (partDefinition.Fields.Any(f => string.Equals(f.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("DisplayName", S["A field with the same Display Name already exists."]); + } - return RedirectToAction(nameof(EditPart), new { id }); + if (string.IsNullOrWhiteSpace(viewModel.Name)) + { + ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); } - [HttpPost, ActionName("EditPart")] - [FormValueRequired("submit.Delete")] - public async Task DeletePart(string id) + if (partDefinition.Fields.Any(f => string.Equals(f.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + ModelState.AddModelError("Name", S["A field with the same Technical Name already exists."]); + } - var partViewModel = await _contentDefinitionService.LoadPartAsync(id); + if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) + { + ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); + } - if (partViewModel == null) - { - return NotFound(); - } + if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); + } - await _contentDefinitionService.RemovePartAsync(id); + if (!ModelState.IsValid) + { + viewModel.Part = partDefinition; + viewModel.Fields = (await _contentDefinitionService.GetFieldsAsync()).Select(x => x.Name).OrderBy(x => x).ToList(); - await _notifier.InformationAsync(H["\"{0}\" has been removed.", partViewModel.DisplayName]); + await _documentStore.CancelAsync(); - return RedirectToAction(nameof(ListParts)); + ViewData["ReturnUrl"] = returnUrl; + return View(viewModel); } - [Admin("ContentTypes/AddFieldsTo/{id}", "AddFieldsTo")] - public async Task AddFieldTo(string id, string returnUrl = null) + await _contentDefinitionService.AddFieldToPartAsync(viewModel.Name, viewModel.DisplayName, viewModel.FieldTypeName, partDefinition.Name); + + await _notifier.SuccessAsync(H["The field \"{0}\" has been added.", viewModel.DisplayName]); + + if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return this.Redirect(returnUrl, true); + } + else + { + return RedirectToAction(nameof(EditField), new { id, viewModel.Name }); + } + } - var fields = (await _contentDefinitionService.GetFieldsAsync()).ToList(); + [Admin("ContentParts/{id}/Fields/{name}/Edit", "EditField")] + public async Task EditField(string id, string name, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (fields.Count == 0) - { - await _notifier.WarningAsync(H["There are no fields."]); + var partViewModel = await _contentDefinitionService.GetPartAsync(id); - return RedirectToAction(nameof(List)); - } + if (partViewModel == null) + { + return NotFound(); + } - var partViewModel = await _contentDefinitionService.LoadPartAsync(id); + var partFieldDefinition = partViewModel.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - if (partViewModel == null) - { - return NotFound(); - } + if (partFieldDefinition?.FieldDefinition?.Name == null + || !(await _contentDefinitionService.GetFieldsAsync()).Any(field => string.Equals(field.Name, partFieldDefinition.FieldDefinition.Name, StringComparison.OrdinalIgnoreCase))) + { + return NotFound(); + } - var viewModel = new AddFieldViewModel - { - Part = partViewModel.PartDefinition, - Fields = fields.Select(field => field.Name).OrderBy(name => name).ToList() - }; + var viewModel = new EditFieldViewModel + { + Name = partFieldDefinition.Name, + Editor = partFieldDefinition.Editor(), + DisplayMode = partFieldDefinition.DisplayMode(), + DisplayName = partFieldDefinition.DisplayName(), + PartFieldDefinition = partFieldDefinition, + Shape = await _contentDefinitionDisplayManager.BuildPartFieldEditorAsync(partFieldDefinition, _updateModelAccessor.ModelUpdater) + }; + + ViewData["ReturnUrl"] = returnUrl; + return View(viewModel); + } - ViewData["ReturnUrl"] = returnUrl; - return View(viewModel); + [HttpPost, ActionName("EditField")] + [FormValueRequired("submit.Save")] + public async Task EditFieldPOST(string id, EditFieldViewModel viewModel, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - [HttpPost, ActionName("AddFieldTo")] - public async Task AddFieldToPOST(AddFieldViewModel viewModel, string id, string returnUrl = null) + if (viewModel == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + return NotFound(); + } - var partViewModel = await _contentDefinitionService.LoadPartAsync(id); + var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - if (partViewModel == null) - { - return NotFound(); - } + if (partViewModel == null) + { + return NotFound(); + } - var fields = (await _contentDefinitionService.GetFieldsAsync()).ToList(); + var field = (await _contentDefinitionManager.LoadPartDefinitionAsync(id)).Fields.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); - if (!fields.Any(field => string.Equals(field.Name, viewModel.FieldTypeName, StringComparison.OrdinalIgnoreCase))) - { - return NotFound(); - } + if (field == null) + { + return NotFound(); + } - var partDefinition = partViewModel.PartDefinition; + viewModel.PartFieldDefinition = field; + if (field.DisplayName() != viewModel.DisplayName) + { + // prevent null reference exception in validation viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - viewModel.Name ??= string.Empty; if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) { ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); } - if (partDefinition.Fields.Any(f => string.Equals(f.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) + if ((await _contentDefinitionService.LoadPartAsync(partViewModel.Name)).PartDefinition.Fields.Any(t => t.Name != viewModel.Name && string.Equals(t.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) { ModelState.AddModelError("DisplayName", S["A field with the same Display Name already exists."]); } - if (string.IsNullOrWhiteSpace(viewModel.Name)) - { - ModelState.AddModelError("Name", S["The Technical Name can't be empty."]); - } - - if (partDefinition.Fields.Any(f => string.Equals(f.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("Name", S["A field with the same Technical Name already exists."]); - } - - if (!string.IsNullOrWhiteSpace(viewModel.Name) && !char.IsLetter(viewModel.Name[0])) - { - ModelState.AddModelError("Name", S["The Technical Name must start with a letter."]); - } - - if (!string.Equals(viewModel.Name, viewModel.Name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError("Name", S["The Technical Name contains invalid characters."]); - } - if (!ModelState.IsValid) { - viewModel.Part = partDefinition; - viewModel.Fields = (await _contentDefinitionService.GetFieldsAsync()).Select(x => x.Name).OrderBy(x => x).ToList(); - + // Calls update to build editor shape with the display name validation failures, and other validation errors. + viewModel.Shape = await _contentDefinitionDisplayManager.UpdatePartFieldEditorAsync(field, _updateModelAccessor.ModelUpdater); await _documentStore.CancelAsync(); ViewData["ReturnUrl"] = returnUrl; return View(viewModel); } - await _contentDefinitionService.AddFieldToPartAsync(viewModel.Name, viewModel.DisplayName, viewModel.FieldTypeName, partDefinition.Name); - - await _notifier.SuccessAsync(H["The field \"{0}\" has been added.", viewModel.DisplayName]); - - if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) - { - return this.Redirect(returnUrl, true); - } - else - { - return RedirectToAction(nameof(EditField), new { id, viewModel.Name }); - } + await _notifier.InformationAsync(H["Display name changed to {0}.", viewModel.DisplayName]); } - [Admin("ContentParts/{id}/Fields/{name}/Edit", "EditField")] - public async Task EditField(string id, string name, string returnUrl = null) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } - - var partViewModel = await _contentDefinitionService.GetPartAsync(id); - - if (partViewModel == null) - { - return NotFound(); - } + await _contentDefinitionService.AlterFieldAsync(partViewModel, viewModel); - var partFieldDefinition = partViewModel.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); + // Refresh the local field variable in case it has been altered + field = (await _contentDefinitionManager.LoadPartDefinitionAsync(id)).Fields.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); - if (partFieldDefinition?.FieldDefinition?.Name == null - || !(await _contentDefinitionService.GetFieldsAsync()).Any(field => string.Equals(field.Name, partFieldDefinition.FieldDefinition.Name, StringComparison.OrdinalIgnoreCase))) - { - return NotFound(); - } + viewModel.Shape = await _contentDefinitionDisplayManager.UpdatePartFieldEditorAsync(field, _updateModelAccessor.ModelUpdater); - var viewModel = new EditFieldViewModel - { - Name = partFieldDefinition.Name, - Editor = partFieldDefinition.Editor(), - DisplayMode = partFieldDefinition.DisplayMode(), - DisplayName = partFieldDefinition.DisplayName(), - PartFieldDefinition = partFieldDefinition, - Shape = await _contentDefinitionDisplayManager.BuildPartFieldEditorAsync(partFieldDefinition, _updateModelAccessor.ModelUpdater) - }; + if (!ModelState.IsValid) + { + await _documentStore.CancelAsync(); ViewData["ReturnUrl"] = returnUrl; return View(viewModel); } - - [HttpPost, ActionName("EditField")] - [FormValueRequired("submit.Save")] - public async Task EditFieldPOST(string id, EditFieldViewModel viewModel, string returnUrl = null) + else { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + await _notifier.SuccessAsync(H["The \"{0}\" field settings have been saved.", field.DisplayName()]); + } - if (viewModel == null) + if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) + { + return this.Redirect(returnUrl, true); + } + else + { + // Redirect to the type editor if a type exists with this name + var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); + if (typeViewModel != null) { - return NotFound(); + return RedirectToAction(nameof(Edit), new { id }); } - var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - - if (partViewModel == null) - { - return NotFound(); - } + return RedirectToAction(nameof(EditPart), new { id }); + } + } - var field = (await _contentDefinitionManager.LoadPartDefinitionAsync(id)).Fields.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); + [HttpPost, ActionName("RemoveFieldFrom")] + public async Task RemoveFieldFromPOST(string id, string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (field == null) - { - return NotFound(); - } + var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - viewModel.PartFieldDefinition = field; + if (partViewModel == null) + { + return NotFound(); + } - if (field.DisplayName() != viewModel.DisplayName) - { - // prevent null reference exception in validation - viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; + var field = partViewModel.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) - { - ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); - } + if (field == null) + { + return NotFound(); + } - if ((await _contentDefinitionService.LoadPartAsync(partViewModel.Name)).PartDefinition.Fields.Any(t => t.Name != viewModel.Name && string.Equals(t.DisplayName().Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("DisplayName", S["A field with the same Display Name already exists."]); - } + await _contentDefinitionService.RemoveFieldFromPartAsync(name, partViewModel.Name); - if (!ModelState.IsValid) - { - // Calls update to build editor shape with the display name validation failures, and other validation errors. - viewModel.Shape = await _contentDefinitionDisplayManager.UpdatePartFieldEditorAsync(field, _updateModelAccessor.ModelUpdater); - await _documentStore.CancelAsync(); + await _notifier.SuccessAsync(H["The \"{0}\" field has been removed.", field.DisplayName()]); - ViewData["ReturnUrl"] = returnUrl; - return View(viewModel); - } + if (await _contentDefinitionService.LoadTypeAsync(id) != null) + { + return RedirectToAction(nameof(Edit), new { id }); + } - await _notifier.InformationAsync(H["Display name changed to {0}.", viewModel.DisplayName]); - } + return RedirectToAction(nameof(EditPart), new { id }); + } - await _contentDefinitionService.AlterFieldAsync(partViewModel, viewModel); + #endregion Parts - // Refresh the local field variable in case it has been altered - field = (await _contentDefinitionManager.LoadPartDefinitionAsync(id)).Fields.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); + #region Type Parts - viewModel.Shape = await _contentDefinitionDisplayManager.UpdatePartFieldEditorAsync(field, _updateModelAccessor.ModelUpdater); + [Admin("ContentTypes/{id}/ContentParts/{name}/Edit", "EditTypePart")] + public async Task EditTypePart(string id, string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); + } - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(id); - ViewData["ReturnUrl"] = returnUrl; - return View(viewModel); - } - else - { - await _notifier.SuccessAsync(H["The \"{0}\" field settings have been saved.", field.DisplayName()]); - } + if (typeDefinition == null) + { + return NotFound(); + } - if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl)) - { - return this.Redirect(returnUrl, true); - } - else - { - // Redirect to the type editor if a type exists with this name - var typeViewModel = await _contentDefinitionService.LoadTypeAsync(id); - if (typeViewModel != null) - { - return RedirectToAction(nameof(Edit), new { id }); - } + var typePartDefinition = typeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - return RedirectToAction(nameof(EditPart), new { id }); - } + if (typePartDefinition == null) + { + return NotFound(); } - [HttpPost, ActionName("RemoveFieldFrom")] - public async Task RemoveFieldFromPOST(string id, string name) + var typePartViewModel = new EditTypePartViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } - - var partViewModel = await _contentDefinitionService.LoadPartAsync(id); - - if (partViewModel == null) - { - return NotFound(); - } - - var field = partViewModel.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - - if (field == null) - { - return NotFound(); - } - - await _contentDefinitionService.RemoveFieldFromPartAsync(name, partViewModel.Name); - - await _notifier.SuccessAsync(H["The \"{0}\" field has been removed.", field.DisplayName()]); - - if (await _contentDefinitionService.LoadTypeAsync(id) != null) - { - return RedirectToAction(nameof(Edit), new { id }); - } + Name = typePartDefinition.Name, + Editor = typePartDefinition.Editor(), + DisplayMode = typePartDefinition.DisplayMode(), + DisplayName = typePartDefinition.DisplayName(), + Description = typePartDefinition.Description(), + TypePartDefinition = typePartDefinition, + Shape = await _contentDefinitionDisplayManager.BuildTypePartEditorAsync(typePartDefinition, _updateModelAccessor.ModelUpdater) + }; + + return View(typePartViewModel); + } - return RedirectToAction(nameof(EditPart), new { id }); + [HttpPost, ActionName("EditTypePart")] + [FormValueRequired("submit.Save")] + public async Task EditTypePartPOST(string id, EditTypePartViewModel viewModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) + { + return Forbid(); } - #endregion Parts - - #region Type Parts - - [Admin("ContentTypes/{id}/ContentParts/{name}/Edit", "EditTypePart")] - public async Task EditTypePart(string id, string name) + if (viewModel == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } - - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(id); - - if (typeDefinition == null) - { - return NotFound(); - } - - var typePartDefinition = typeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - - if (typePartDefinition == null) - { - return NotFound(); - } + return NotFound(); + } - var typePartViewModel = new EditTypePartViewModel - { - Name = typePartDefinition.Name, - Editor = typePartDefinition.Editor(), - DisplayMode = typePartDefinition.DisplayMode(), - DisplayName = typePartDefinition.DisplayName(), - Description = typePartDefinition.Description(), - TypePartDefinition = typePartDefinition, - Shape = await _contentDefinitionDisplayManager.BuildTypePartEditorAsync(typePartDefinition, _updateModelAccessor.ModelUpdater) - }; + var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(id); - return View(typePartViewModel); + if (typeDefinition == null) + { + return NotFound(); } - [HttpPost, ActionName("EditTypePart")] - [FormValueRequired("submit.Save")] - public async Task EditTypePartPOST(string id, EditTypePartViewModel viewModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.EditContentTypes)) - { - return Forbid(); - } + var part = typeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); - if (viewModel == null) - { - return NotFound(); - } + if (part == null) + { + return NotFound(); + } - var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(id); + viewModel.TypePartDefinition = part; - if (typeDefinition == null) + if (part.PartDefinition.IsReusable()) + { + if (part.DisplayName() != viewModel.DisplayName) { - return NotFound(); - } - - var part = typeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); + // Prevent null reference exception in validation + viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - if (part == null) - { - return NotFound(); - } + if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) + { + ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); + } - viewModel.TypePartDefinition = part; + if (typeDefinition.Parts.Any(t => t.Name != viewModel.Name && string.Equals(t.DisplayName()?.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError("DisplayName", S["A part with the same Display Name already exists."]); + } - if (part.PartDefinition.IsReusable()) - { - if (part.DisplayName() != viewModel.DisplayName) + if (!ModelState.IsValid) { - // Prevent null reference exception in validation - viewModel.DisplayName = viewModel.DisplayName?.Trim() ?? string.Empty; - - if (string.IsNullOrWhiteSpace(viewModel.DisplayName)) - { - ModelState.AddModelError("DisplayName", S["The Display Name can't be empty."]); - } - - if (typeDefinition.Parts.Any(t => t.Name != viewModel.Name && string.Equals(t.DisplayName()?.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError("DisplayName", S["A part with the same Display Name already exists."]); - } - - if (!ModelState.IsValid) - { - viewModel.Shape = await _contentDefinitionDisplayManager.UpdateTypePartEditorAsync(part, _updateModelAccessor.ModelUpdater); - await _documentStore.CancelAsync(); - return View(viewModel); - } + viewModel.Shape = await _contentDefinitionDisplayManager.UpdateTypePartEditorAsync(part, _updateModelAccessor.ModelUpdater); + await _documentStore.CancelAsync(); + return View(viewModel); } } + } - await _contentDefinitionService.AlterTypePartAsync(viewModel); - - // Refresh the local part variable in case it has been altered - part = (await _contentDefinitionManager.LoadTypeDefinitionAsync(id)).Parts.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); + await _contentDefinitionService.AlterTypePartAsync(viewModel); - viewModel.Shape = await _contentDefinitionDisplayManager.UpdateTypePartEditorAsync(part, _updateModelAccessor.ModelUpdater); + // Refresh the local part variable in case it has been altered + part = (await _contentDefinitionManager.LoadTypeDefinitionAsync(id)).Parts.FirstOrDefault(x => string.Equals(x.Name, viewModel.Name, StringComparison.OrdinalIgnoreCase)); - if (!ModelState.IsValid) - { - await _documentStore.CancelAsync(); - return View(viewModel); - } - else - { - await _notifier.SuccessAsync(H["The \"{0}\" part settings have been saved.", part.DisplayName()]); - } + viewModel.Shape = await _contentDefinitionDisplayManager.UpdateTypePartEditorAsync(part, _updateModelAccessor.ModelUpdater); - return RedirectToAction(nameof(Edit), new { id }); + if (!ModelState.IsValid) + { + await _documentStore.CancelAsync(); + return View(viewModel); + } + else + { + await _notifier.SuccessAsync(H["The \"{0}\" part settings have been saved.", part.DisplayName()]); } - #endregion Type Parts + return RedirectToAction(nameof(Edit), new { id }); } + + #endregion Type Parts } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentSource.cs index 2f88284061e..2f455501e7b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentSource.cs @@ -4,42 +4,41 @@ using OrchardCore.ContentManagement; using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public class ContentDefinitionDeploymentSource : IDeploymentSource { - public class ContentDefinitionDeploymentSource : IDeploymentSource + private readonly IContentDefinitionStore _contentDefinitionStore; + + public ContentDefinitionDeploymentSource(IContentDefinitionStore contentDefinitionStore) { - private readonly IContentDefinitionStore _contentDefinitionStore; + _contentDefinitionStore = contentDefinitionStore; + } - public ContentDefinitionDeploymentSource(IContentDefinitionStore contentDefinitionStore) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not ContentDefinitionDeploymentStep contentDefinitionStep) { - _contentDefinitionStore = contentDefinitionStore; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var contentTypeDefinitionRecord = await _contentDefinitionStore.LoadContentDefinitionAsync(); + + var contentTypes = contentDefinitionStep.IncludeAll + ? contentTypeDefinitionRecord.ContentTypeDefinitionRecords + : contentTypeDefinitionRecord.ContentTypeDefinitionRecords + .Where(x => contentDefinitionStep.ContentTypes.Contains(x.Name)); + + var contentParts = contentDefinitionStep.IncludeAll + ? contentTypeDefinitionRecord.ContentPartDefinitionRecords + : contentTypeDefinitionRecord.ContentPartDefinitionRecords + .Where(x => contentDefinitionStep.ContentParts.Contains(x.Name)); + + result.Steps.Add(new JsonObject { - if (step is not ContentDefinitionDeploymentStep contentDefinitionStep) - { - return; - } - - var contentTypeDefinitionRecord = await _contentDefinitionStore.LoadContentDefinitionAsync(); - - var contentTypes = contentDefinitionStep.IncludeAll - ? contentTypeDefinitionRecord.ContentTypeDefinitionRecords - : contentTypeDefinitionRecord.ContentTypeDefinitionRecords - .Where(x => contentDefinitionStep.ContentTypes.Contains(x.Name)); - - var contentParts = contentDefinitionStep.IncludeAll - ? contentTypeDefinitionRecord.ContentPartDefinitionRecords - : contentTypeDefinitionRecord.ContentPartDefinitionRecords - .Where(x => contentDefinitionStep.ContentParts.Contains(x.Name)); - - result.Steps.Add(new JsonObject - { - ["name"] = "ContentDefinition", - ["ContentTypes"] = JArray.FromObject(contentTypes), - ["ContentParts"] = JArray.FromObject(contentParts), - }); - } + ["name"] = "ContentDefinition", + ["ContentTypes"] = JArray.FromObject(contentTypes), + ["ContentParts"] = JArray.FromObject(contentParts), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStep.cs index 4ab892f8924..c365001a8d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStep.cs @@ -1,21 +1,20 @@ using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +/// +/// Adds selected content definitions to a . +/// +public class ContentDefinitionDeploymentStep : DeploymentStep { - /// - /// Adds selected content definitions to a . - /// - public class ContentDefinitionDeploymentStep : DeploymentStep + public ContentDefinitionDeploymentStep() { - public ContentDefinitionDeploymentStep() - { - Name = "ContentDefinition"; - } + Name = "ContentDefinition"; + } - public bool IncludeAll { get; set; } + public bool IncludeAll { get; set; } - public string[] ContentTypes { get; set; } + public string[] ContentTypes { get; set; } - public string[] ContentParts { get; set; } - } + public string[] ContentParts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStepDriver.cs index c6a465bf21f..1f7f6b7fc01 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ContentDefinitionDeploymentStepDriver.cs @@ -5,54 +5,53 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public sealed class ContentDefinitionDeploymentStepDriver : DisplayDriver { - public sealed class ContentDefinitionDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(ContentDefinitionDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(ContentDefinitionDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("ContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(ContentDefinitionDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(ContentDefinitionDeploymentStep step, BuildEditorContext context) + { + return Initialize("ContentDefinitionDeploymentStep_Fields_Edit", model => { - return Initialize("ContentDefinitionDeploymentStep_Fields_Edit", model => - { - model.ContentParts = step.ContentParts; - model.ContentTypes = step.ContentTypes; - model.IncludeAll = step.IncludeAll; - }).Location("Content"); - } + model.ContentParts = step.ContentParts; + model.ContentTypes = step.ContentTypes; + model.IncludeAll = step.IncludeAll; + }).Location("Content"); + } + + public override async Task UpdateAsync(ContentDefinitionDeploymentStep step, UpdateEditorContext context) + { + // Initializes the value to empty otherwise the model is not updated if no type is selected. + step.ContentTypes = []; + step.ContentParts = []; + + await context.Updater.TryUpdateModelAsync( + step, + Prefix, + x => x.ContentTypes, + x => x.ContentParts, + x => x.IncludeAll); - public override async Task UpdateAsync(ContentDefinitionDeploymentStep step, UpdateEditorContext context) + // don't have the selected option if include all + if (step.IncludeAll) { - // Initializes the value to empty otherwise the model is not updated if no type is selected. step.ContentTypes = []; step.ContentParts = []; - - await context.Updater.TryUpdateModelAsync( - step, - Prefix, - x => x.ContentTypes, - x => x.ContentParts, - x => x.IncludeAll); - - // don't have the selected option if include all - if (step.IncludeAll) - { - step.ContentTypes = []; - step.ContentParts = []; - } - else - { - step.ContentParts = step.ContentParts.Distinct().ToArray(); - } - - return Edit(step, context); } + else + { + step.ContentParts = step.ContentParts.Distinct().ToArray(); + } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentSource.cs index b54de8c1e0c..07974426034 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentSource.cs @@ -2,25 +2,24 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public class DeleteContentDefinitionDeploymentSource : IDeploymentSource { - public class DeleteContentDefinitionDeploymentSource : IDeploymentSource + public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (step is not DeleteContentDefinitionDeploymentStep deleteContentDefinitionStep) { - if (step is not DeleteContentDefinitionDeploymentStep deleteContentDefinitionStep) - { - return Task.CompletedTask; - } - - result.Steps.Add(new JsonObject - { - ["name"] = "DeleteContentDefinition", - ["ContentTypes"] = JArray.FromObject(deleteContentDefinitionStep.ContentTypes), - ["ContentParts"] = JArray.FromObject(deleteContentDefinitionStep.ContentParts), - }); - return Task.CompletedTask; } + + result.Steps.Add(new JsonObject + { + ["name"] = "DeleteContentDefinition", + ["ContentTypes"] = JArray.FromObject(deleteContentDefinitionStep.ContentTypes), + ["ContentParts"] = JArray.FromObject(deleteContentDefinitionStep.ContentParts), + }); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStep.cs index 5523f0bf867..949d36c057b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStep.cs @@ -1,19 +1,18 @@ using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +/// +/// Deletes selected content definitions to a . +/// +public class DeleteContentDefinitionDeploymentStep : DeploymentStep { - /// - /// Deletes selected content definitions to a . - /// - public class DeleteContentDefinitionDeploymentStep : DeploymentStep + public DeleteContentDefinitionDeploymentStep() { - public DeleteContentDefinitionDeploymentStep() - { - Name = "DeleteContentDefinition"; - } + Name = "DeleteContentDefinition"; + } - public string[] ContentTypes { get; set; } = []; + public string[] ContentTypes { get; set; } = []; - public string[] ContentParts { get; set; } = []; - } + public string[] ContentParts { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStepDriver.cs index 95d61fd7f86..e00d39160f6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/DeleteContentDefinitionDeploymentStepDriver.cs @@ -5,40 +5,39 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public sealed class DeleteContentDefinitionDeploymentStepDriver : DisplayDriver { - public sealed class DeleteContentDefinitionDeploymentStepDriver : DisplayDriver + private static readonly char[] _separator = [' ', ',']; + + public override Task DisplayAsync(DeleteContentDefinitionDeploymentStep step, BuildDisplayContext context) { - private static readonly char[] _separator = [' ', ',']; + return + CombineAsync( + View("DeleteContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("DeleteContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(DeleteContentDefinitionDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("DeleteContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("DeleteContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } - - public override IDisplayResult Edit(DeleteContentDefinitionDeploymentStep step, BuildEditorContext context) - { - return Initialize("DeleteContentDefinitionDeploymentStep_Fields_Edit", model => - { - model.ContentParts = string.Join(", ", step.ContentParts); - model.ContentTypes = string.Join(", ", step.ContentTypes); - }).Location("Content"); - } - - public override async Task UpdateAsync(DeleteContentDefinitionDeploymentStep step, UpdateEditorContext context) + public override IDisplayResult Edit(DeleteContentDefinitionDeploymentStep step, BuildEditorContext context) + { + return Initialize("DeleteContentDefinitionDeploymentStep_Fields_Edit", model => { - var model = new DeleteContentDefinitionStepViewModel(); + model.ContentParts = string.Join(", ", step.ContentParts); + model.ContentTypes = string.Join(", ", step.ContentTypes); + }).Location("Content"); + } + + public override async Task UpdateAsync(DeleteContentDefinitionDeploymentStep step, UpdateEditorContext context) + { + var model = new DeleteContentDefinitionStepViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - step.ContentTypes = model.ContentTypes.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - step.ContentParts = model.ContentParts.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + step.ContentTypes = model.ContentTypes.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + step.ContentParts = model.ContentParts.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentSource.cs index 0b35e8c324d..3c3d9239f82 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentSource.cs @@ -4,42 +4,41 @@ using OrchardCore.ContentManagement; using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public class ReplaceContentDefinitionDeploymentSource : IDeploymentSource { - public class ReplaceContentDefinitionDeploymentSource : IDeploymentSource + private readonly IContentDefinitionStore _contentDefinitionStore; + + public ReplaceContentDefinitionDeploymentSource(IContentDefinitionStore contentDefinitionStore) { - private readonly IContentDefinitionStore _contentDefinitionStore; + _contentDefinitionStore = contentDefinitionStore; + } - public ReplaceContentDefinitionDeploymentSource(IContentDefinitionStore contentDefinitionStore) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not ReplaceContentDefinitionDeploymentStep replaceContentDefinitionStep) { - _contentDefinitionStore = contentDefinitionStore; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var contentTypeDefinitionRecord = await _contentDefinitionStore.LoadContentDefinitionAsync(); + + var contentTypes = replaceContentDefinitionStep.IncludeAll + ? contentTypeDefinitionRecord.ContentTypeDefinitionRecords + : contentTypeDefinitionRecord.ContentTypeDefinitionRecords + .Where(x => replaceContentDefinitionStep.ContentTypes.Contains(x.Name)); + + var contentParts = replaceContentDefinitionStep.IncludeAll + ? contentTypeDefinitionRecord.ContentPartDefinitionRecords + : contentTypeDefinitionRecord.ContentPartDefinitionRecords + .Where(x => replaceContentDefinitionStep.ContentParts.Contains(x.Name)); + + result.Steps.Add(new JsonObject { - if (step is not ReplaceContentDefinitionDeploymentStep replaceContentDefinitionStep) - { - return; - } - - var contentTypeDefinitionRecord = await _contentDefinitionStore.LoadContentDefinitionAsync(); - - var contentTypes = replaceContentDefinitionStep.IncludeAll - ? contentTypeDefinitionRecord.ContentTypeDefinitionRecords - : contentTypeDefinitionRecord.ContentTypeDefinitionRecords - .Where(x => replaceContentDefinitionStep.ContentTypes.Contains(x.Name)); - - var contentParts = replaceContentDefinitionStep.IncludeAll - ? contentTypeDefinitionRecord.ContentPartDefinitionRecords - : contentTypeDefinitionRecord.ContentPartDefinitionRecords - .Where(x => replaceContentDefinitionStep.ContentParts.Contains(x.Name)); - - result.Steps.Add(new JsonObject - { - ["name"] = "ReplaceContentDefinition", - ["ContentTypes"] = JArray.FromObject(contentTypes), - ["ContentParts"] = JArray.FromObject(contentParts), - }); - } + ["name"] = "ReplaceContentDefinition", + ["ContentTypes"] = JArray.FromObject(contentTypes), + ["ContentParts"] = JArray.FromObject(contentParts), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStep.cs index 90fed2dbe8b..4ac94c008f8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStep.cs @@ -1,18 +1,17 @@ using OrchardCore.Deployment; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public class ReplaceContentDefinitionDeploymentStep : DeploymentStep { - public class ReplaceContentDefinitionDeploymentStep : DeploymentStep + public ReplaceContentDefinitionDeploymentStep() { - public ReplaceContentDefinitionDeploymentStep() - { - Name = "ReplaceContentDefinition"; - } + Name = "ReplaceContentDefinition"; + } - public bool IncludeAll { get; set; } + public bool IncludeAll { get; set; } - public string[] ContentTypes { get; set; } + public string[] ContentTypes { get; set; } - public string[] ContentParts { get; set; } - } + public string[] ContentParts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStepDriver.cs index 471ee2be29e..acecd03ee16 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Deployment/ReplaceContentDefinitionDeploymentStepDriver.cs @@ -5,54 +5,53 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Deployment +namespace OrchardCore.ContentTypes.Deployment; + +public sealed class ReplaceContentDefinitionDeploymentStepDriver : DisplayDriver { - public sealed class ReplaceContentDefinitionDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(ReplaceContentDefinitionDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(ReplaceContentDefinitionDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ReplaceContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ReplaceContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("ReplaceContentDefinitionDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ReplaceContentDefinitionDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(ReplaceContentDefinitionDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(ReplaceContentDefinitionDeploymentStep step, BuildEditorContext context) + { + return Initialize("ReplaceContentDefinitionDeploymentStep_Fields_Edit", model => { - return Initialize("ReplaceContentDefinitionDeploymentStep_Fields_Edit", model => - { - model.ContentParts = step.ContentParts; - model.ContentTypes = step.ContentTypes; - model.IncludeAll = step.IncludeAll; - }).Location("Content"); - } + model.ContentParts = step.ContentParts; + model.ContentTypes = step.ContentTypes; + model.IncludeAll = step.IncludeAll; + }).Location("Content"); + } + + public override async Task UpdateAsync(ReplaceContentDefinitionDeploymentStep step, UpdateEditorContext context) + { + // Initializes the value to empty otherwise the model is not updated if no type is selected. + step.ContentTypes = []; + step.ContentParts = []; + + await context.Updater.TryUpdateModelAsync( + step, + Prefix, + x => x.ContentTypes, + x => x.ContentParts, + x => x.IncludeAll); - public override async Task UpdateAsync(ReplaceContentDefinitionDeploymentStep step, UpdateEditorContext context) + // don't have the selected option if include all + if (step.IncludeAll) { - // Initializes the value to empty otherwise the model is not updated if no type is selected. step.ContentTypes = []; step.ContentParts = []; - - await context.Updater.TryUpdateModelAsync( - step, - Prefix, - x => x.ContentTypes, - x => x.ContentParts, - x => x.IncludeAll); - - // don't have the selected option if include all - if (step.IncludeAll) - { - step.ContentTypes = []; - step.ContentParts = []; - } - else - { - step.ContentParts = step.ContentParts.Distinct().ToArray(); - } - - return Edit(step, context); } + else + { + step.ContentParts = step.ContentParts.Distinct().ToArray(); + } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentDefinitionDisplayCoordinator.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentDefinitionDisplayCoordinator.cs index e0489fd525b..f88069f33f7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentDefinitionDisplayCoordinator.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentDefinitionDisplayCoordinator.cs @@ -5,108 +5,107 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.Modules; -namespace OrchardCore.ContentTypes.Editors +namespace OrchardCore.ContentTypes.Editors; + +public class ContentDefinitionDisplayCoordinator : IContentDefinitionDisplayHandler { - public class ContentDefinitionDisplayCoordinator : IContentDefinitionDisplayHandler - { - private readonly IEnumerable _typeDisplayDrivers; - private readonly IEnumerable _typePartDisplayDrivers; - private readonly IEnumerable _partDisplayDrivers; - private readonly IEnumerable _partFieldDisplayDrivers; - private readonly ILogger _logger; + private readonly IEnumerable _typeDisplayDrivers; + private readonly IEnumerable _typePartDisplayDrivers; + private readonly IEnumerable _partDisplayDrivers; + private readonly IEnumerable _partFieldDisplayDrivers; + private readonly ILogger _logger; - public ContentDefinitionDisplayCoordinator( - IEnumerable typeDisplayDrivers, - IEnumerable typePartDisplayDrivers, - IEnumerable partDisplayDrivers, - IEnumerable partFieldDisplayDrivers, - ILogger logger) - { - _partFieldDisplayDrivers = partFieldDisplayDrivers; - _partDisplayDrivers = partDisplayDrivers; - _typePartDisplayDrivers = typePartDisplayDrivers; - _typeDisplayDrivers = typeDisplayDrivers; - _logger = logger; - } + public ContentDefinitionDisplayCoordinator( + IEnumerable typeDisplayDrivers, + IEnumerable typePartDisplayDrivers, + IEnumerable partDisplayDrivers, + IEnumerable partFieldDisplayDrivers, + ILogger logger) + { + _partFieldDisplayDrivers = partFieldDisplayDrivers; + _partDisplayDrivers = partDisplayDrivers; + _typePartDisplayDrivers = typePartDisplayDrivers; + _typeDisplayDrivers = typeDisplayDrivers; + _logger = logger; + } - public Task BuildTypeEditorAsync(ContentTypeDefinition model, BuildEditorContext context) + public Task BuildTypeEditorAsync(ContentTypeDefinition model, BuildEditorContext context) + { + return _typeDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _typeDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.BuildEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.BuildEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public Task UpdateTypeEditorAsync(ContentTypeDefinition model, UpdateTypeEditorContext context) + public Task UpdateTypeEditorAsync(ContentTypeDefinition model, UpdateTypeEditorContext context) + { + return _typeDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _typeDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.UpdateEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.UpdateEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public Task BuildTypePartEditorAsync(ContentTypePartDefinition model, BuildEditorContext context) + public Task BuildTypePartEditorAsync(ContentTypePartDefinition model, BuildEditorContext context) + { + return _typePartDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _typePartDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.BuildEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.BuildEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public Task UpdateTypePartEditorAsync(ContentTypePartDefinition model, UpdateTypePartEditorContext context) + public Task UpdateTypePartEditorAsync(ContentTypePartDefinition model, UpdateTypePartEditorContext context) + { + return _typePartDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _typePartDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.UpdateEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.UpdateEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public Task BuildPartEditorAsync(ContentPartDefinition model, BuildEditorContext context) + public Task BuildPartEditorAsync(ContentPartDefinition model, BuildEditorContext context) + { + return _partDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _partDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.BuildEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.BuildEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public Task UpdatePartEditorAsync(ContentPartDefinition model, UpdatePartEditorContext context) + public Task UpdatePartEditorAsync(ContentPartDefinition model, UpdatePartEditorContext context) + { + return _partDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - return _partDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.UpdateEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.UpdateEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public async Task BuildPartFieldEditorAsync(ContentPartFieldDefinition model, BuildEditorContext context) + public async Task BuildPartFieldEditorAsync(ContentPartFieldDefinition model, BuildEditorContext context) + { + await _partFieldDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - await _partFieldDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.BuildEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.BuildEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); + } - public async Task UpdatePartFieldEditorAsync(ContentPartFieldDefinition model, UpdatePartFieldEditorContext context) + public async Task UpdatePartFieldEditorAsync(ContentPartFieldDefinition model, UpdatePartFieldEditorContext context) + { + await _partFieldDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => { - await _partFieldDisplayDrivers.InvokeAsync(async (contentDisplay, model, context) => - { - var result = await contentDisplay.UpdateEditorAsync(model, context); - if (result != null) - await result.ApplyAsync(context); - }, model, context, _logger); - } + var result = await contentDisplay.UpdateEditorAsync(model, context); + if (result != null) + await result.ApplyAsync(context); + }, model, context, _logger); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentPartSettingsDisplayDriver.cs index 4a5cc9d7f0c..2da8d5b4a8c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentPartSettingsDisplayDriver.cs @@ -5,36 +5,35 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Editors +namespace OrchardCore.ContentTypes.Editors; + +public sealed class ContentPartSettingsDisplayDriver : ContentPartDefinitionDisplayDriver { - public sealed class ContentPartSettingsDisplayDriver : ContentPartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartDefinition contentPartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartDefinition contentPartDefinition, BuildEditorContext context) + return Initialize("ContentPartSettings_Edit", model => { - return Initialize("ContentPartSettings_Edit", model => - { - var settings = contentPartDefinition.GetSettings(); + var settings = contentPartDefinition.GetSettings(); - model.Attachable = settings.Attachable; - model.Reusable = settings.Reusable; - model.Description = settings.Description; - model.DisplayName = settings.DisplayName; - model.ContentPartDefinition = contentPartDefinition; - }).Location("Content"); - } + model.Attachable = settings.Attachable; + model.Reusable = settings.Reusable; + model.Description = settings.Description; + model.DisplayName = settings.DisplayName; + model.ContentPartDefinition = contentPartDefinition; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartDefinition contentPartDefinition, UpdatePartEditorContext context) - { - var model = new ContentPartSettingsViewModel(); + public override async Task UpdateAsync(ContentPartDefinition contentPartDefinition, UpdatePartEditorContext context) + { + var model = new ContentPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.Attachable(model.Attachable); - context.Builder.Reusable(model.Reusable); - context.Builder.WithDescription(model.Description); - context.Builder.WithDisplayName(model.DisplayName); + context.Builder.Attachable(model.Attachable); + context.Builder.Reusable(model.Reusable); + context.Builder.WithDescription(model.Description); + context.Builder.WithDisplayName(model.DisplayName); - return Edit(contentPartDefinition, context); - } + return Edit(contentPartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypePartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypePartSettingsDisplayDriver.cs index c4072f6f557..a56f28fb086 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypePartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypePartSettingsDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Editors +namespace OrchardCore.ContentTypes.Editors; + +public sealed class ContentTypePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class ContentTypePartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition model, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition model, BuildEditorContext context) - { - return Shape("ContentTypePartSettings_Edit", new ShapeViewModel(model)).Location("Content"); - } + return Shape("ContentTypePartSettings_Edit", new ShapeViewModel(model)).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypeSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypeSettingsDisplayDriver.cs index bf04e26193c..825583ba801 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypeSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/ContentTypeSettingsDisplayDriver.cs @@ -9,120 +9,119 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Editors +namespace OrchardCore.ContentTypes.Editors; + +public sealed class ContentTypeSettingsDisplayDriver : ContentTypeDefinitionDisplayDriver { - public sealed class ContentTypeSettingsDisplayDriver : ContentTypeDefinitionDisplayDriver - { - private static readonly ContentTypeDefinitionDriverOptions _defaultOptions = new(); - private readonly IStereotypeService _stereotypeService; - private readonly ContentTypeDefinitionOptions _options; + private static readonly ContentTypeDefinitionDriverOptions _defaultOptions = new(); + private readonly IStereotypeService _stereotypeService; + private readonly ContentTypeDefinitionOptions _options; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public ContentTypeSettingsDisplayDriver( - IStringLocalizer stringLocalizer, - IOptions options, - IStereotypeService stereotypeService) - { - S = stringLocalizer; - _options = options.Value; - _stereotypeService = stereotypeService; - } + public ContentTypeSettingsDisplayDriver( + IStringLocalizer stringLocalizer, + IOptions options, + IStereotypeService stereotypeService) + { + S = stringLocalizer; + _options = options.Value; + _stereotypeService = stereotypeService; + } - public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) + { + return Initialize("ContentTypeSettings_Edit", async model => { - return Initialize("ContentTypeSettings_Edit", async model => - { - var settings = contentTypeDefinition.GetSettings(); - model.Creatable = settings.Creatable; - model.Listable = settings.Listable; - model.Draftable = settings.Draftable; - model.Versionable = settings.Versionable; - model.Securable = settings.Securable; - model.Stereotype = settings.Stereotype; - model.Description = settings.Description; - model.Options = await GetOptionsAsync(contentTypeDefinition, settings.Stereotype); - }).Location("Content:5"); - } + var settings = contentTypeDefinition.GetSettings(); + model.Creatable = settings.Creatable; + model.Listable = settings.Listable; + model.Draftable = settings.Draftable; + model.Versionable = settings.Versionable; + model.Securable = settings.Securable; + model.Stereotype = settings.Stereotype; + model.Description = settings.Description; + model.Options = await GetOptionsAsync(contentTypeDefinition, settings.Stereotype); + }).Location("Content:5"); + } - public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) - { - var model = new ContentTypeSettingsViewModel(); + public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) + { + var model = new ContentTypeSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - var stereotype = model.Stereotype?.Trim(); - context.Builder.WithDescription(model.Description); - context.Builder.Stereotype(stereotype); + var stereotype = model.Stereotype?.Trim(); + context.Builder.WithDescription(model.Description); + context.Builder.Stereotype(stereotype); - if (!IsAlphaNumericOrEmpty(stereotype)) - { - context.Updater.ModelState.AddModelError(nameof(ContentTypeSettingsViewModel.Stereotype), S["The stereotype should be alphanumeric."]); - } + if (!IsAlphaNumericOrEmpty(stereotype)) + { + context.Updater.ModelState.AddModelError(nameof(ContentTypeSettingsViewModel.Stereotype), S["The stereotype should be alphanumeric."]); + } - var options = await GetOptionsAsync(contentTypeDefinition, stereotype); + var options = await GetOptionsAsync(contentTypeDefinition, stereotype); - Apply(context, model, options); + Apply(context, model, options); - return Edit(contentTypeDefinition, context); - } + return Edit(contentTypeDefinition, context); + } - private static void Apply(UpdateTypeEditorContext context, ContentTypeSettingsViewModel model, ContentTypeDefinitionDriverOptions options) + private static void Apply(UpdateTypeEditorContext context, ContentTypeSettingsViewModel model, ContentTypeDefinitionDriverOptions options) + { + if (options.ShowVersionable) { - if (options.ShowVersionable) - { - context.Builder.Versionable(model.Versionable); - } - - if (options.ShowCreatable) - { - context.Builder.Creatable(model.Creatable); - } - - if (options.ShowSecurable) - { - context.Builder.Securable(model.Securable); - } - - if (options.ShowListable) - { - context.Builder.Listable(model.Listable); - } + context.Builder.Versionable(model.Versionable); } - private async Task GetOptionsAsync(ContentTypeDefinition contentTypeDefinition, string stereotype) + if (options.ShowCreatable) { - var options = _defaultOptions; + context.Builder.Creatable(model.Creatable); + } - if (contentTypeDefinition.Name != null - && _options.ContentTypes.TryGetValue(contentTypeDefinition.Name, out var typeOptions)) - { - options = typeOptions; - } + if (options.ShowSecurable) + { + context.Builder.Securable(model.Securable); + } - if (stereotype != null - && _options.Stereotypes.TryGetValue(stereotype, out var stereotypesOptions)) - { - options = stereotypesOptions; - } + if (options.ShowListable) + { + context.Builder.Listable(model.Listable); + } + } - options.Stereotypes = await _stereotypeService.GetStereotypesAsync(); + private async Task GetOptionsAsync(ContentTypeDefinition contentTypeDefinition, string stereotype) + { + var options = _defaultOptions; - return options; + if (contentTypeDefinition.Name != null + && _options.ContentTypes.TryGetValue(contentTypeDefinition.Name, out var typeOptions)) + { + options = typeOptions; } - private static bool IsAlphaNumericOrEmpty(string value) + if (stereotype != null + && _options.Stereotypes.TryGetValue(stereotype, out var stereotypesOptions)) { - if (string.IsNullOrEmpty(value)) - { - return true; - } + options = stereotypesOptions; + } + + options.Stereotypes = await _stereotypeService.GetStereotypesAsync(); - var startWithLetter = char.IsLetter(value[0]); + return options; + } - return value.Length == 1 - ? startWithLetter - : startWithLetter && value.Skip(1).All(c => char.IsLetterOrDigit(c)); + private static bool IsAlphaNumericOrEmpty(string value) + { + if (string.IsNullOrEmpty(value)) + { + return true; } + + var startWithLetter = char.IsLetter(value[0]); + + return value.Length == 1 + ? startWithLetter + : startWithLetter && value.Skip(1).All(c => char.IsLetterOrDigit(c)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs index bc7045bc6bd..fa6a1a086b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentDefinitionDisplayManager.cs @@ -11,122 +11,177 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Modules; -namespace OrchardCore.ContentTypes.Editors +namespace OrchardCore.ContentTypes.Editors; + +public class DefaultContentDefinitionDisplayManager : BaseDisplayManager, IContentDefinitionDisplayManager { - public class DefaultContentDefinitionDisplayManager : BaseDisplayManager, IContentDefinitionDisplayManager + private readonly IEnumerable _handlers; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IShapeFactory _shapeFactory; + private readonly ILayoutAccessor _layoutAccessor; + private readonly ILogger _logger; + + public DefaultContentDefinitionDisplayManager( + IEnumerable handlers, + IContentDefinitionManager contentDefinitionManager, + IShapeFactory shapeFactory, + IEnumerable placementProviders, + ILogger logger, + ILayoutAccessor layoutAccessor + ) : base(shapeFactory, placementProviders) { - private readonly IEnumerable _handlers; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IShapeFactory _shapeFactory; - private readonly ILayoutAccessor _layoutAccessor; - private readonly ILogger _logger; - - public DefaultContentDefinitionDisplayManager( - IEnumerable handlers, - IContentDefinitionManager contentDefinitionManager, - IShapeFactory shapeFactory, - IEnumerable placementProviders, - ILogger logger, - ILayoutAccessor layoutAccessor - ) : base(shapeFactory, placementProviders) - { - _handlers = handlers; - _contentDefinitionManager = contentDefinitionManager; - _shapeFactory = shapeFactory; - _layoutAccessor = layoutAccessor; - _logger = logger; - } - - public async Task BuildTypeEditorAsync(ContentTypeDefinition contentTypeDefinition, IUpdateModel updater, string groupId) - { - ArgumentNullException.ThrowIfNull(contentTypeDefinition); + _handlers = handlers; + _contentDefinitionManager = contentDefinitionManager; + _shapeFactory = shapeFactory; + _layoutAccessor = layoutAccessor; + _logger = logger; + } + + public async Task BuildTypeEditorAsync(ContentTypeDefinition contentTypeDefinition, IUpdateModel updater, string groupId) + { + ArgumentNullException.ThrowIfNull(contentTypeDefinition); + + var contentTypeDefinitionShape = await CreateContentShapeAsync("ContentTypeDefinition_Edit"); + contentTypeDefinitionShape.Properties["ContentTypeDefinition"] = contentTypeDefinition; + + var typeContext = new BuildEditorContext( + contentTypeDefinitionShape, + groupId, + false, + "", + _shapeFactory, + await _layoutAccessor.GetLayoutAsync(), + updater + ); + + await BindPlacementAsync(typeContext); + + await _handlers.InvokeAsync((handler, contentTypeDefinition, typeContext) => handler.BuildTypeEditorAsync(contentTypeDefinition, typeContext), contentTypeDefinition, typeContext, _logger); - var contentTypeDefinitionShape = await CreateContentShapeAsync("ContentTypeDefinition_Edit"); - contentTypeDefinitionShape.Properties["ContentTypeDefinition"] = contentTypeDefinition; + return contentTypeDefinitionShape; + } + + public async Task UpdateTypeEditorAsync(ContentTypeDefinition contentTypeDefinition, IUpdateModel updater, string groupId) + { + ArgumentNullException.ThrowIfNull(contentTypeDefinition); + + var contentTypeDefinitionShape = await CreateContentShapeAsync("ContentTypeDefinition_Edit"); + contentTypeDefinitionShape.Properties["ContentTypeDefinition"] = contentTypeDefinition; + + var layout = await _layoutAccessor.GetLayoutAsync(); - var typeContext = new BuildEditorContext( + await _contentDefinitionManager.AlterTypeDefinitionAsync(contentTypeDefinition.Name, async typeBuilder => + { + var typeContext = new UpdateTypeEditorContext( + typeBuilder, contentTypeDefinitionShape, groupId, false, - "", _shapeFactory, - await _layoutAccessor.GetLayoutAsync(), + layout, updater ); await BindPlacementAsync(typeContext); - await _handlers.InvokeAsync((handler, contentTypeDefinition, typeContext) => handler.BuildTypeEditorAsync(contentTypeDefinition, typeContext), contentTypeDefinition, typeContext, _logger); + await _handlers.InvokeAsync((handler, contentTypeDefinition, typeContext) => handler.UpdateTypeEditorAsync(contentTypeDefinition, typeContext), contentTypeDefinition, typeContext, _logger); + }); - return contentTypeDefinitionShape; - } + return contentTypeDefinitionShape; + } - public async Task UpdateTypeEditorAsync(ContentTypeDefinition contentTypeDefinition, IUpdateModel updater, string groupId) - { - ArgumentNullException.ThrowIfNull(contentTypeDefinition); + public async Task BuildPartEditorAsync(ContentPartDefinition contentPartDefinition, IUpdateModel updater, string groupId) + { + ArgumentNullException.ThrowIfNull(contentPartDefinition); - var contentTypeDefinitionShape = await CreateContentShapeAsync("ContentTypeDefinition_Edit"); - contentTypeDefinitionShape.Properties["ContentTypeDefinition"] = contentTypeDefinition; + var contentPartDefinitionShape = await CreateContentShapeAsync("ContentPartDefinition_Edit"); - var layout = await _layoutAccessor.GetLayoutAsync(); + var partContext = new BuildEditorContext( + contentPartDefinitionShape, + groupId, + false, + "", + _shapeFactory, + await _layoutAccessor.GetLayoutAsync(), + updater + ); - await _contentDefinitionManager.AlterTypeDefinitionAsync(contentTypeDefinition.Name, async typeBuilder => - { - var typeContext = new UpdateTypeEditorContext( - typeBuilder, - contentTypeDefinitionShape, - groupId, - false, - _shapeFactory, - layout, - updater - ); + await BindPlacementAsync(partContext); - await BindPlacementAsync(typeContext); + await _handlers.InvokeAsync((handler, contentPartDefinition, partContext) => handler.BuildPartEditorAsync(contentPartDefinition, partContext), contentPartDefinition, partContext, _logger); - await _handlers.InvokeAsync((handler, contentTypeDefinition, typeContext) => handler.UpdateTypeEditorAsync(contentTypeDefinition, typeContext), contentTypeDefinition, typeContext, _logger); - }); + return contentPartDefinitionShape; + } - return contentTypeDefinitionShape; - } + public async Task UpdatePartEditorAsync(ContentPartDefinition contentPartDefinition, IUpdateModel updater, string groupId) + { + ArgumentNullException.ThrowIfNull(contentPartDefinition); - public async Task BuildPartEditorAsync(ContentPartDefinition contentPartDefinition, IUpdateModel updater, string groupId) - { - ArgumentNullException.ThrowIfNull(contentPartDefinition); + var contentPartDefinitionShape = await CreateContentShapeAsync("ContentPartDefinition_Edit"); - var contentPartDefinitionShape = await CreateContentShapeAsync("ContentPartDefinition_Edit"); + UpdatePartEditorContext partContext = null; + var layout = await _layoutAccessor.GetLayoutAsync(); - var partContext = new BuildEditorContext( + await _contentDefinitionManager.AlterPartDefinitionAsync(contentPartDefinition.Name, async partBuilder => + { + partContext = new UpdatePartEditorContext( + partBuilder, contentPartDefinitionShape, groupId, false, - "", _shapeFactory, - await _layoutAccessor.GetLayoutAsync(), + layout, updater ); await BindPlacementAsync(partContext); - await _handlers.InvokeAsync((handler, contentPartDefinition, partContext) => handler.BuildPartEditorAsync(contentPartDefinition, partContext), contentPartDefinition, partContext, _logger); + await _handlers.InvokeAsync((handler, contentPartDefinition, partContext) => handler.UpdatePartEditorAsync(contentPartDefinition, partContext), contentPartDefinition, partContext, _logger); + }); - return contentPartDefinitionShape; - } + return contentPartDefinitionShape; + } - public async Task UpdatePartEditorAsync(ContentPartDefinition contentPartDefinition, IUpdateModel updater, string groupId) - { - ArgumentNullException.ThrowIfNull(contentPartDefinition); + public async Task BuildTypePartEditorAsync(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater, string groupId = "") + { + ArgumentNullException.ThrowIfNull(contentTypePartDefinition); + + var typePartDefinitionShape = await CreateContentShapeAsync("ContentTypePartDefinition_Edit"); + typePartDefinitionShape.Properties["ContentPart"] = contentTypePartDefinition; + + var partContext = new BuildEditorContext( + typePartDefinitionShape, + groupId, + false, + "", + _shapeFactory, + await _layoutAccessor.GetLayoutAsync(), + updater + ); + + await BindPlacementAsync(partContext); + + await _handlers.InvokeAsync((handler, contentTypePartDefinition, partContext) => handler.BuildTypePartEditorAsync(contentTypePartDefinition, partContext), contentTypePartDefinition, partContext, _logger); + + return typePartDefinitionShape; + } - var contentPartDefinitionShape = await CreateContentShapeAsync("ContentPartDefinition_Edit"); + public async Task UpdateTypePartEditorAsync(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater, string groupId = "") + { + ArgumentNullException.ThrowIfNull(contentTypePartDefinition); - UpdatePartEditorContext partContext = null; - var layout = await _layoutAccessor.GetLayoutAsync(); + var typePartDefinitionShape = await CreateContentShapeAsync("ContentTypePartDefinition_Edit"); + var layout = await _layoutAccessor.GetLayoutAsync(); - await _contentDefinitionManager.AlterPartDefinitionAsync(contentPartDefinition.Name, async partBuilder => + await _contentDefinitionManager.AlterTypeDefinitionAsync(contentTypePartDefinition.ContentTypeDefinition.Name, typeBuilder => + { + return typeBuilder.WithPartAsync(contentTypePartDefinition.Name, async typePartBuilder => { - partContext = new UpdatePartEditorContext( - partBuilder, - contentPartDefinitionShape, + typePartDefinitionShape.Properties["ContentPart"] = contentTypePartDefinition; + + var partContext = new UpdateTypePartEditorContext( + typePartBuilder, + typePartDefinitionShape, groupId, false, _shapeFactory, @@ -136,124 +191,68 @@ await _contentDefinitionManager.AlterPartDefinitionAsync(contentPartDefinition.N await BindPlacementAsync(partContext); - await _handlers.InvokeAsync((handler, contentPartDefinition, partContext) => handler.UpdatePartEditorAsync(contentPartDefinition, partContext), contentPartDefinition, partContext, _logger); + await _handlers.InvokeAsync((handler, contentTypePartDefinition, partContext) => handler.UpdateTypePartEditorAsync(contentTypePartDefinition, partContext), contentTypePartDefinition, partContext, _logger); }); + }); - return contentPartDefinitionShape; - } - - public async Task BuildTypePartEditorAsync(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater, string groupId = "") - { - ArgumentNullException.ThrowIfNull(contentTypePartDefinition); - - var typePartDefinitionShape = await CreateContentShapeAsync("ContentTypePartDefinition_Edit"); - typePartDefinitionShape.Properties["ContentPart"] = contentTypePartDefinition; - - var partContext = new BuildEditorContext( - typePartDefinitionShape, - groupId, - false, - "", - _shapeFactory, - await _layoutAccessor.GetLayoutAsync(), - updater - ); - - await BindPlacementAsync(partContext); - - await _handlers.InvokeAsync((handler, contentTypePartDefinition, partContext) => handler.BuildTypePartEditorAsync(contentTypePartDefinition, partContext), contentTypePartDefinition, partContext, _logger); - - return typePartDefinitionShape; - } - - public async Task UpdateTypePartEditorAsync(ContentTypePartDefinition contentTypePartDefinition, IUpdateModel updater, string groupId = "") - { - ArgumentNullException.ThrowIfNull(contentTypePartDefinition); + return typePartDefinitionShape; + } - var typePartDefinitionShape = await CreateContentShapeAsync("ContentTypePartDefinition_Edit"); - var layout = await _layoutAccessor.GetLayoutAsync(); + public async Task BuildPartFieldEditorAsync(ContentPartFieldDefinition contentPartFieldDefinition, IUpdateModel updater, string groupId = "") + { + ArgumentNullException.ThrowIfNull(contentPartFieldDefinition); - await _contentDefinitionManager.AlterTypeDefinitionAsync(contentTypePartDefinition.ContentTypeDefinition.Name, typeBuilder => - { - return typeBuilder.WithPartAsync(contentTypePartDefinition.Name, async typePartBuilder => - { - typePartDefinitionShape.Properties["ContentPart"] = contentTypePartDefinition; - - var partContext = new UpdateTypePartEditorContext( - typePartBuilder, - typePartDefinitionShape, - groupId, - false, - _shapeFactory, - layout, - updater - ); - - await BindPlacementAsync(partContext); - - await _handlers.InvokeAsync((handler, contentTypePartDefinition, partContext) => handler.UpdateTypePartEditorAsync(contentTypePartDefinition, partContext), contentTypePartDefinition, partContext, _logger); - }); - }); + var partFieldDefinitionShape = await CreateContentShapeAsync("ContentPartFieldDefinition_Edit"); + partFieldDefinitionShape.Properties["ContentField"] = contentPartFieldDefinition; - return typePartDefinitionShape; - } + var fieldContext = new BuildEditorContext( + partFieldDefinitionShape, + groupId, + false, + "", + _shapeFactory, + await _layoutAccessor.GetLayoutAsync(), + updater + ); - public async Task BuildPartFieldEditorAsync(ContentPartFieldDefinition contentPartFieldDefinition, IUpdateModel updater, string groupId = "") - { - ArgumentNullException.ThrowIfNull(contentPartFieldDefinition); + await BindPlacementAsync(fieldContext); - var partFieldDefinitionShape = await CreateContentShapeAsync("ContentPartFieldDefinition_Edit"); - partFieldDefinitionShape.Properties["ContentField"] = contentPartFieldDefinition; + await _handlers.InvokeAsync((handler, contentPartFieldDefinition, fieldContext) => handler.BuildPartFieldEditorAsync(contentPartFieldDefinition, fieldContext), contentPartFieldDefinition, fieldContext, _logger); - var fieldContext = new BuildEditorContext( - partFieldDefinitionShape, - groupId, - false, - "", - _shapeFactory, - await _layoutAccessor.GetLayoutAsync(), - updater - ); + return partFieldDefinitionShape; + } - await BindPlacementAsync(fieldContext); + public async Task UpdatePartFieldEditorAsync(ContentPartFieldDefinition contentPartFieldDefinition, IUpdateModel updater, string groupId = "") + { + ArgumentNullException.ThrowIfNull(contentPartFieldDefinition); - await _handlers.InvokeAsync((handler, contentPartFieldDefinition, fieldContext) => handler.BuildPartFieldEditorAsync(contentPartFieldDefinition, fieldContext), contentPartFieldDefinition, fieldContext, _logger); + var contentPartDefinition = contentPartFieldDefinition.PartDefinition; + var partFieldDefinitionShape = await CreateContentShapeAsync("ContentPartFieldDefinition_Edit"); - return partFieldDefinitionShape; - } + var layout = await _layoutAccessor.GetLayoutAsync(); - public async Task UpdatePartFieldEditorAsync(ContentPartFieldDefinition contentPartFieldDefinition, IUpdateModel updater, string groupId = "") + await _contentDefinitionManager.AlterPartDefinitionAsync(contentPartDefinition.Name, partBuilder => { - ArgumentNullException.ThrowIfNull(contentPartFieldDefinition); + return partBuilder.WithFieldAsync(contentPartFieldDefinition.Name, async partFieldBuilder => + { + partFieldDefinitionShape.Properties["ContentField"] = contentPartFieldDefinition; - var contentPartDefinition = contentPartFieldDefinition.PartDefinition; - var partFieldDefinitionShape = await CreateContentShapeAsync("ContentPartFieldDefinition_Edit"); + var fieldContext = new UpdatePartFieldEditorContext( + partFieldBuilder, + partFieldDefinitionShape, + groupId, + false, + _shapeFactory, + layout, + updater + ); - var layout = await _layoutAccessor.GetLayoutAsync(); + await BindPlacementAsync(fieldContext); - await _contentDefinitionManager.AlterPartDefinitionAsync(contentPartDefinition.Name, partBuilder => - { - return partBuilder.WithFieldAsync(contentPartFieldDefinition.Name, async partFieldBuilder => - { - partFieldDefinitionShape.Properties["ContentField"] = contentPartFieldDefinition; - - var fieldContext = new UpdatePartFieldEditorContext( - partFieldBuilder, - partFieldDefinitionShape, - groupId, - false, - _shapeFactory, - layout, - updater - ); - - await BindPlacementAsync(fieldContext); - - await _handlers.InvokeAsync((handler, contentPartFieldDefinition, fieldContext) => handler.UpdatePartFieldEditorAsync(contentPartFieldDefinition, fieldContext), contentPartFieldDefinition, fieldContext, _logger); - }); + await _handlers.InvokeAsync((handler, contentPartFieldDefinition, fieldContext) => handler.UpdatePartFieldEditorAsync(contentPartFieldDefinition, fieldContext), contentPartFieldDefinition, fieldContext, _logger); }); + }); - return partFieldDefinitionShape; - } + return partFieldDefinitionShape; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentTypeDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentTypeDisplayDriver.cs index a543ffe2d48..0f8920f4cc9 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentTypeDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Editors/DefaultContentTypeDisplayDriver.cs @@ -5,40 +5,39 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.Editors -{ - public sealed class DefaultContentTypeDisplayDriver : ContentTypeDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; +namespace OrchardCore.ContentTypes.Editors; - public DefaultContentTypeDisplayDriver(IStringLocalizer localizer) - { - S = localizer; - } +public sealed class DefaultContentTypeDisplayDriver : ContentTypeDefinitionDisplayDriver +{ + internal readonly IStringLocalizer S; - public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) - { - return Initialize("ContentType_Edit", model => - { - model.DisplayName = contentTypeDefinition.DisplayName; - model.Name = contentTypeDefinition.Name; - }).Location("Content"); - } + public DefaultContentTypeDisplayDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) + public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) + { + return Initialize("ContentType_Edit", model => { - var model = new ContentTypeViewModel(); + model.DisplayName = contentTypeDefinition.DisplayName; + model.Name = contentTypeDefinition.Name; + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) + { + var model = new ContentTypeViewModel(); - context.Builder.DisplayedAs(model.DisplayName); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (string.IsNullOrWhiteSpace(model.DisplayName)) - { - context.Updater.ModelState.AddModelError("DisplayName", S["The Content Type name can't be empty."]); - } + context.Builder.DisplayedAs(model.DisplayName); - return Edit(contentTypeDefinition, context); + if (string.IsNullOrWhiteSpace(model.DisplayName)) + { + context.Updater.ModelState.AddModelError("DisplayName", S["The Content Type name can't be empty."]); } + + return Edit(contentTypeDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Drivers/GraphQLContentTypePartSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Drivers/GraphQLContentTypePartSettingsDriver.cs index 461527e0d65..e3f45a24e2b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Drivers/GraphQLContentTypePartSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Drivers/GraphQLContentTypePartSettingsDriver.cs @@ -8,52 +8,51 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.ContentTypes.GraphQL.Drivers +namespace OrchardCore.ContentTypes.GraphQL.Drivers; + +public sealed class GraphQLContentTypePartSettingsDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class GraphQLContentTypePartSettingsDriver : ContentTypePartDefinitionDisplayDriver + private readonly GraphQLContentOptions _contentOptions; + + public GraphQLContentTypePartSettingsDriver(IOptions optionsAccessor) { - private readonly GraphQLContentOptions _contentOptions; + _contentOptions = optionsAccessor.Value; + } - public GraphQLContentTypePartSettingsDriver(IOptions optionsAccessor) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + if (contentTypePartDefinition.ContentTypeDefinition.Name == contentTypePartDefinition.PartDefinition.Name) { - _contentOptions = optionsAccessor.Value; + return null; } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("GraphQLContentTypePartSettings_Edit", async model => { - if (contentTypePartDefinition.ContentTypeDefinition.Name == contentTypePartDefinition.PartDefinition.Name) - { - return null; - } + model.Definition = contentTypePartDefinition; + model.Options = _contentOptions; + model.Settings = contentTypePartDefinition.GetSettings(); - return Initialize("GraphQLContentTypePartSettings_Edit", async model => + if (!context.Updater.ModelState.IsValid) { - model.Definition = contentTypePartDefinition; - model.Options = _contentOptions; - model.Settings = contentTypePartDefinition.GetSettings(); - - if (!context.Updater.ModelState.IsValid) - { - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Settings); - } - }).Location("Content"); - } + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Settings); + } + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if (contentTypePartDefinition.ContentTypeDefinition.Name == contentTypePartDefinition.PartDefinition.Name) { - if (contentTypePartDefinition.ContentTypeDefinition.Name == contentTypePartDefinition.PartDefinition.Name) - { - return null; - } + return null; + } - var model = new GraphQLContentTypePartSettingsViewModel(); + var model = new GraphQLContentTypePartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.Settings); + await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.Settings); - context.Builder.WithSettings(model.Settings); + context.Builder.WithSettings(model.Settings); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Startup.cs index a42ed632403..ca0ac000574 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/Startup.cs @@ -3,15 +3,14 @@ using OrchardCore.ContentTypes.GraphQL.Drivers; using OrchardCore.Modules; -namespace OrchardCore.ContentTypes.GraphQL +namespace OrchardCore.ContentTypes.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypePartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypePartSettingsViewModel.cs index 172af75e3a8..a7d8096281a 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypePartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypePartSettingsViewModel.cs @@ -2,12 +2,11 @@ using OrchardCore.ContentManagement.GraphQL.Settings; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.GraphQL.ViewModels +namespace OrchardCore.ContentTypes.GraphQL.ViewModels; + +public class GraphQLContentTypePartSettingsViewModel { - public class GraphQLContentTypePartSettingsViewModel - { - public GraphQLContentOptions Options { get; set; } - public GraphQLContentTypePartSettings Settings { get; set; } - public ContentTypePartDefinition Definition { get; set; } - } + public GraphQLContentOptions Options { get; set; } + public GraphQLContentTypePartSettings Settings { get; set; } + public ContentTypePartDefinition Definition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypeSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypeSettingsViewModel.cs index 9dcad2ad663..0a8b73490b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypeSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/GraphQL/ViewModels/GraphQLContentTypeSettingsViewModel.cs @@ -3,16 +3,15 @@ using OrchardCore.ContentManagement.GraphQL.Settings; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.GraphQL.ViewModels +namespace OrchardCore.ContentTypes.GraphQL.ViewModels; + +public class GraphQLContentTypeSettingsViewModel { - public class GraphQLContentTypeSettingsViewModel - { - public GraphQLContentTypeSettings Settings { get; set; } + public GraphQLContentTypeSettings Settings { get; set; } - [BindNever] - public GraphQLContentOptions Options { get; set; } + [BindNever] + public GraphQLContentOptions Options { get; set; } - [BindNever] - public ContentTypeDefinition Definition { get; set; } - } + [BindNever] + public ContentTypeDefinition Definition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ContentDefinitionStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ContentDefinitionStep.cs index 81e64f830b8..10fb5ae7bb0 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ContentDefinitionStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ContentDefinitionStep.cs @@ -8,104 +8,103 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.ContentTypes.RecipeSteps +namespace OrchardCore.ContentTypes.RecipeSteps; + +/// +/// This recipe step creates content definitions. +/// +public sealed class ContentDefinitionStep : IRecipeStepHandler { - /// - /// This recipe step creates content definitions. - /// - public sealed class ContentDefinitionStep : IRecipeStepHandler + private readonly IContentDefinitionManager _contentDefinitionManager; + + internal readonly IStringLocalizer S; + + public ContentDefinitionStep( + IContentDefinitionManager contentDefinitionManager, + IStringLocalizer stringLocalizer) + { + _contentDefinitionManager = contentDefinitionManager; + S = stringLocalizer; + } + + public async Task ExecuteAsync(RecipeExecutionContext context) { - private readonly IContentDefinitionManager _contentDefinitionManager; + if (!string.Equals(context.Name, "ContentDefinition", StringComparison.OrdinalIgnoreCase)) + { + return; + } - internal readonly IStringLocalizer S; + var step = context.Step.ToObject(); - public ContentDefinitionStep( - IContentDefinitionManager contentDefinitionManager, - IStringLocalizer stringLocalizer) + foreach (var contentType in step.ContentTypes) { - _contentDefinitionManager = contentDefinitionManager; - S = stringLocalizer; + var newType = await _contentDefinitionManager.LoadTypeDefinitionAsync(contentType.Name) + ?? new ContentTypeDefinition(contentType.Name, contentType.DisplayName); + + await UpdateContentTypeAsync(newType, contentType, context); } - public async Task ExecuteAsync(RecipeExecutionContext context) + foreach (var contentPart in step.ContentParts) { - if (!string.Equals(context.Name, "ContentDefinition", StringComparison.OrdinalIgnoreCase)) - { - return; - } + var newPart = await _contentDefinitionManager.LoadPartDefinitionAsync(contentPart.Name) + ?? new ContentPartDefinition(contentPart.Name); - var step = context.Step.ToObject(); + await UpdateContentPartAsync(newPart, contentPart, context); + } + } - foreach (var contentType in step.ContentTypes) + private Task UpdateContentTypeAsync(ContentTypeDefinition type, ContentTypeDefinitionRecord record, RecipeExecutionContext context) + { + return _contentDefinitionManager.AlterTypeDefinitionAsync(type.Name, builder => + { + if (!string.IsNullOrEmpty(record.DisplayName)) { - var newType = await _contentDefinitionManager.LoadTypeDefinitionAsync(contentType.Name) - ?? new ContentTypeDefinition(contentType.Name, contentType.DisplayName); - - await UpdateContentTypeAsync(newType, contentType, context); + builder.DisplayedAs(record.DisplayName); + builder.MergeSettings(record.Settings); } - foreach (var contentPart in step.ContentParts) + foreach (var part in record.ContentTypePartDefinitionRecords) { - var newPart = await _contentDefinitionManager.LoadPartDefinitionAsync(contentPart.Name) - ?? new ContentPartDefinition(contentPart.Name); + if (string.IsNullOrEmpty(part.PartName)) + { + context.Errors.Add(S["Unable to add content-part to the '{0}' content-type. The part name cannot be null or empty.", type.Name]); + + continue; + } - await UpdateContentPartAsync(newPart, contentPart, context); + builder.WithPart(part.Name, part.PartName, partBuilder => partBuilder.MergeSettings(part.Settings)); } - } + }); + } - private Task UpdateContentTypeAsync(ContentTypeDefinition type, ContentTypeDefinitionRecord record, RecipeExecutionContext context) + private Task UpdateContentPartAsync(ContentPartDefinition part, ContentPartDefinitionRecord record, RecipeExecutionContext context) + { + return _contentDefinitionManager.AlterPartDefinitionAsync(part.Name, builder => { - return _contentDefinitionManager.AlterTypeDefinitionAsync(type.Name, builder => - { - if (!string.IsNullOrEmpty(record.DisplayName)) - { - builder.DisplayedAs(record.DisplayName); - builder.MergeSettings(record.Settings); - } + builder.MergeSettings(record.Settings); - foreach (var part in record.ContentTypePartDefinitionRecords) + foreach (var field in record.ContentPartFieldDefinitionRecords) + { + if (string.IsNullOrEmpty(field.Name)) { - if (string.IsNullOrEmpty(part.PartName)) - { - context.Errors.Add(S["Unable to add content-part to the '{0}' content-type. The part name cannot be null or empty.", type.Name]); - - continue; - } + context.Errors.Add(S["Unable to add content-field to the '{0}' content-part. The part name cannot be null or empty.", part.Name]); - builder.WithPart(part.Name, part.PartName, partBuilder => partBuilder.MergeSettings(part.Settings)); + continue; } - }); - } - - private Task UpdateContentPartAsync(ContentPartDefinition part, ContentPartDefinitionRecord record, RecipeExecutionContext context) - { - return _contentDefinitionManager.AlterPartDefinitionAsync(part.Name, builder => - { - builder.MergeSettings(record.Settings); - foreach (var field in record.ContentPartFieldDefinitionRecords) + builder.WithField(field.Name, fieldBuilder => { - if (string.IsNullOrEmpty(field.Name)) - { - context.Errors.Add(S["Unable to add content-field to the '{0}' content-part. The part name cannot be null or empty.", part.Name]); - - continue; - } - - builder.WithField(field.Name, fieldBuilder => - { - fieldBuilder.OfType(field.FieldName); - fieldBuilder.MergeSettings(field.Settings); - }); - } - }); - } + fieldBuilder.OfType(field.FieldName); + fieldBuilder.MergeSettings(field.Settings); + }); + } + }); + } - private sealed class ContentDefinitionStepModel - { - public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; + private sealed class ContentDefinitionStepModel + { + public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; - public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; - } + public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/DeleteContentDefinitionStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/DeleteContentDefinitionStep.cs index 5c892321d5c..ba76f975ae1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/DeleteContentDefinitionStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/DeleteContentDefinitionStep.cs @@ -5,46 +5,45 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.ContentTypes.RecipeSteps +namespace OrchardCore.ContentTypes.RecipeSteps; + +/// +/// This recipe step deletes content definition records. +/// +public sealed class DeleteContentDefinitionStep : IRecipeStepHandler { - /// - /// This recipe step deletes content definition records. - /// - public sealed class DeleteContentDefinitionStep : IRecipeStepHandler + private readonly IContentDefinitionManager _contentDefinitionManager; + + public DeleteContentDefinitionStep(IContentDefinitionManager contentDefinitionManager) { - private readonly IContentDefinitionManager _contentDefinitionManager; + _contentDefinitionManager = contentDefinitionManager; + } - public DeleteContentDefinitionStep(IContentDefinitionManager contentDefinitionManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "DeleteContentDefinition", StringComparison.OrdinalIgnoreCase)) { - _contentDefinitionManager = contentDefinitionManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + var step = context.Step.ToObject(); + + foreach (var contentType in step.ContentTypes) { - if (!string.Equals(context.Name, "DeleteContentDefinition", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var step = context.Step.ToObject(); - - foreach (var contentType in step.ContentTypes) - { - // The content definition manager tests existence before trying to delete. - await _contentDefinitionManager.DeleteTypeDefinitionAsync(contentType); - } - - foreach (var contentPart in step.ContentParts) - { - // The content definition manager tests existence before trying to delete. - await _contentDefinitionManager.DeletePartDefinitionAsync(contentPart); - } + // The content definition manager tests existence before trying to delete. + await _contentDefinitionManager.DeleteTypeDefinitionAsync(contentType); } - private sealed class DeleteContentDefinitionStepModel + foreach (var contentPart in step.ContentParts) { - public string[] ContentTypes { get; set; } = []; - public string[] ContentParts { get; set; } = []; + // The content definition manager tests existence before trying to delete. + await _contentDefinitionManager.DeletePartDefinitionAsync(contentPart); } } + + private sealed class DeleteContentDefinitionStepModel + { + public string[] ContentTypes { get; set; } = []; + public string[] ContentParts { get; set; } = []; + } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ReplaceContentDefinitionStep.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ReplaceContentDefinitionStep.cs index c4fad2a735d..151f23ef10c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ReplaceContentDefinitionStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/RecipeSteps/ReplaceContentDefinitionStep.cs @@ -6,85 +6,84 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.ContentTypes.RecipeSteps +namespace OrchardCore.ContentTypes.RecipeSteps; + +/// +/// This recipe step replaces content definition records. +/// +public sealed class ReplaceContentDefinitionStep : IRecipeStepHandler { - /// - /// This recipe step replaces content definition records. - /// - public sealed class ReplaceContentDefinitionStep : IRecipeStepHandler + private readonly IContentDefinitionManager _contentDefinitionManager; + + public ReplaceContentDefinitionStep(IContentDefinitionManager contentDefinitionManager) { - private readonly IContentDefinitionManager _contentDefinitionManager; + _contentDefinitionManager = contentDefinitionManager; + } - public ReplaceContentDefinitionStep(IContentDefinitionManager contentDefinitionManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "ReplaceContentDefinition", StringComparison.OrdinalIgnoreCase)) { - _contentDefinitionManager = contentDefinitionManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + var step = context.Step.ToObject(); + + // Delete existing parts first, as deleting them later will clear any imported content types using them. + foreach (var contentPart in step.ContentParts) { - if (!string.Equals(context.Name, "ReplaceContentDefinition", StringComparison.OrdinalIgnoreCase)) - { - return; - } + await _contentDefinitionManager.DeletePartDefinitionAsync(contentPart.Name); + } - var step = context.Step.ToObject(); + foreach (var contentType in step.ContentTypes) + { + await _contentDefinitionManager.DeleteTypeDefinitionAsync(contentType.Name); + await AlterContentTypeAsync(contentType); + } - // Delete existing parts first, as deleting them later will clear any imported content types using them. - foreach (var contentPart in step.ContentParts) - { - await _contentDefinitionManager.DeletePartDefinitionAsync(contentPart.Name); - } + foreach (var contentPart in step.ContentParts) + { + await AlterContentPartAsync(contentPart); + } + } - foreach (var contentType in step.ContentTypes) + private async Task AlterContentTypeAsync(ContentTypeDefinitionRecord record) + { + await _contentDefinitionManager.AlterTypeDefinitionAsync(record.Name, builder => + { + if (!string.IsNullOrEmpty(record.DisplayName)) { - await _contentDefinitionManager.DeleteTypeDefinitionAsync(contentType.Name); - await AlterContentTypeAsync(contentType); + builder.DisplayedAs(record.DisplayName); + builder.MergeSettings(record.Settings); } - foreach (var contentPart in step.ContentParts) + foreach (var part in record.ContentTypePartDefinitionRecords) { - await AlterContentPartAsync(contentPart); + builder.WithPart(part.Name, part.PartName, partBuilder => partBuilder.MergeSettings(part.Settings)); } - } + }); + } - private async Task AlterContentTypeAsync(ContentTypeDefinitionRecord record) + private async Task AlterContentPartAsync(ContentPartDefinitionRecord record) + { + await _contentDefinitionManager.AlterPartDefinitionAsync(record.Name, builder => { - await _contentDefinitionManager.AlterTypeDefinitionAsync(record.Name, builder => - { - if (!string.IsNullOrEmpty(record.DisplayName)) - { - builder.DisplayedAs(record.DisplayName); - builder.MergeSettings(record.Settings); - } + builder.MergeSettings(record.Settings); - foreach (var part in record.ContentTypePartDefinitionRecords) - { - builder.WithPart(part.Name, part.PartName, partBuilder => partBuilder.MergeSettings(part.Settings)); - } - }); - } - - private async Task AlterContentPartAsync(ContentPartDefinitionRecord record) - { - await _contentDefinitionManager.AlterPartDefinitionAsync(record.Name, builder => + foreach (var field in record.ContentPartFieldDefinitionRecords) { - builder.MergeSettings(record.Settings); - - foreach (var field in record.ContentPartFieldDefinitionRecords) + builder.WithField(field.Name, fieldBuilder => { - builder.WithField(field.Name, fieldBuilder => - { - fieldBuilder.OfType(field.FieldName); - fieldBuilder.MergeSettings(field.Settings); - }); - } - }); - } + fieldBuilder.OfType(field.FieldName); + fieldBuilder.MergeSettings(field.Settings); + }); + } + }); + } - private sealed class ReplaceContentDefinitionStepModel - { - public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; - public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; - } + private sealed class ReplaceContentDefinitionStepModel + { + public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; + public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Recipes/LuceneRecipeEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Recipes/LuceneRecipeEventHandler.cs index 3b19ecc1668..dc82f59f332 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Recipes/LuceneRecipeEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Recipes/LuceneRecipeEventHandler.cs @@ -4,47 +4,32 @@ using OrchardCore.Recipes.Events; using OrchardCore.Recipes.Models; -namespace OrchardCore.ContentTypes +namespace OrchardCore.ContentTypes; + +/// +/// This handler provides backward compatibility with ContentIndexSettings that have been migrated to LuceneContentIndexSettings. +/// +public class LuceneRecipeEventHandler : IRecipeEventHandler { - /// - /// This handler provides backward compatibility with ContentIndexSettings that have been migrated to LuceneContentIndexSettings. - /// - public class LuceneRecipeEventHandler : IRecipeEventHandler - { - public RecipeExecutionContext Context { get; private set; } + public RecipeExecutionContext Context { get; private set; } - public Task RecipeStepExecutedAsync(RecipeExecutionContext context) => Task.CompletedTask; + public Task RecipeStepExecutedAsync(RecipeExecutionContext context) => Task.CompletedTask; - public Task ExecutionFailedAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; + public Task ExecutionFailedAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; - public Task RecipeExecutedAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; + public Task RecipeExecutedAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; - public Task RecipeExecutingAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; + public Task RecipeExecutingAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask; - public Task RecipeStepExecutingAsync(RecipeExecutionContext context) + public Task RecipeStepExecutingAsync(RecipeExecutionContext context) + { + if (context.Name == "ReplaceContentDefinition" || context.Name == "ContentDefinition") { - if (context.Name == "ReplaceContentDefinition" || context.Name == "ContentDefinition") - { - var step = context.Step.ToObject(); + var step = context.Step.ToObject(); - foreach (var contentType in step.ContentTypes) - { - foreach (var partDefinition in contentType.ContentTypePartDefinitionRecords) - { - if (partDefinition.Settings != null) - { - if (partDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingPartSettings) && - !partDefinition.Settings.ContainsKey("LuceneContentIndexSettings")) - { - partDefinition.Settings.Add("LuceneContentIndexSettings", existingPartSettings); - } - - partDefinition.Settings.Remove("ContentIndexSettings"); - } - } - } - - foreach (var partDefinition in step.ContentParts) + foreach (var contentType in step.ContentTypes) + { + foreach (var partDefinition in contentType.ContentTypePartDefinitionRecords) { if (partDefinition.Settings != null) { @@ -55,34 +40,48 @@ public Task RecipeStepExecutingAsync(RecipeExecutionContext context) } partDefinition.Settings.Remove("ContentIndexSettings"); + } + } + } + + foreach (var partDefinition in step.ContentParts) + { + if (partDefinition.Settings != null) + { + if (partDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingPartSettings) && + !partDefinition.Settings.ContainsKey("LuceneContentIndexSettings")) + { + partDefinition.Settings.Add("LuceneContentIndexSettings", existingPartSettings); + } - foreach (var fieldDefinition in partDefinition.ContentPartFieldDefinitionRecords) + partDefinition.Settings.Remove("ContentIndexSettings"); + + foreach (var fieldDefinition in partDefinition.ContentPartFieldDefinitionRecords) + { + if (fieldDefinition.Settings != null) { - if (fieldDefinition.Settings != null) + if (fieldDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingFieldSettings) && + !fieldDefinition.Settings.ContainsKey("LuceneContentIndexSettings")) { - if (fieldDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingFieldSettings) && - !fieldDefinition.Settings.ContainsKey("LuceneContentIndexSettings")) - { - fieldDefinition.Settings.Add("LuceneContentIndexSettings", existingFieldSettings); - } - - fieldDefinition.Settings.Remove("ContentIndexSettings"); + fieldDefinition.Settings.Add("LuceneContentIndexSettings", existingFieldSettings); } + + fieldDefinition.Settings.Remove("ContentIndexSettings"); } } } - - context.Step = JObject.FromObject(step); } - return Task.CompletedTask; + context.Step = JObject.FromObject(step); } - private sealed class ContentDefinitionStepModel - { - public string Name { get; set; } - public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; - public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; - } + return Task.CompletedTask; + } + + private sealed class ContentDefinitionStepModel + { + public string Name { get; set; } + public ContentTypeDefinitionRecord[] ContentTypes { get; set; } = []; + public ContentPartDefinitionRecord[] ContentParts { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/ContentDefinitionService.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/ContentDefinitionService.cs index d3c2baa9752..7a7de9d86ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/ContentDefinitionService.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/ContentDefinitionService.cs @@ -14,512 +14,511 @@ using OrchardCore.Modules; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.ContentTypes.Services +namespace OrchardCore.ContentTypes.Services; + +public class ContentDefinitionService : IContentDefinitionService { - public class ContentDefinitionService : IContentDefinitionService + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IEnumerable _contentDefinitionEventHandlers; + private readonly IEnumerable _contentPartTypes; + private readonly IEnumerable _contentFieldTypes; + protected readonly IStringLocalizer S; + private readonly ILogger _logger; + + public ContentDefinitionService( + IContentDefinitionManager contentDefinitionManager, + IEnumerable contentDefinitionEventHandlers, + IEnumerable contentParts, + IEnumerable contentFields, + IOptions contentOptions, + ILogger logger, + IStringLocalizer localizer) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IEnumerable _contentDefinitionEventHandlers; - private readonly IEnumerable _contentPartTypes; - private readonly IEnumerable _contentFieldTypes; - protected readonly IStringLocalizer S; - private readonly ILogger _logger; - - public ContentDefinitionService( - IContentDefinitionManager contentDefinitionManager, - IEnumerable contentDefinitionEventHandlers, - IEnumerable contentParts, - IEnumerable contentFields, - IOptions contentOptions, - ILogger logger, - IStringLocalizer localizer) - { - _contentDefinitionManager = contentDefinitionManager; - _contentDefinitionEventHandlers = contentDefinitionEventHandlers; - - foreach (var element in contentParts.Select(x => x.GetType())) - { - logger.LogWarning("The content part '{ContentPart}' should not be registered in DI. Use AddContentPart instead.", element); - } + _contentDefinitionManager = contentDefinitionManager; + _contentDefinitionEventHandlers = contentDefinitionEventHandlers; - foreach (var element in contentFields.Select(x => x.GetType())) - { - logger.LogWarning("The content field '{ContentField}' should not be registered in DI. Use AddContentField instead.", element); - } + foreach (var element in contentParts.Select(x => x.GetType())) + { + logger.LogWarning("The content part '{ContentPart}' should not be registered in DI. Use AddContentPart instead.", element); + } - // TODO: This code can be removed in a future release and rationalized to only use ContentPartOptions. - _contentPartTypes = contentParts.Select(cp => cp.GetType()) - .Union(contentOptions.Value.ContentPartOptions.Select(cpo => cpo.Type)); + foreach (var element in contentFields.Select(x => x.GetType())) + { + logger.LogWarning("The content field '{ContentField}' should not be registered in DI. Use AddContentField instead.", element); + } - // TODO: This code can be removed in a future release and rationalized to only use ContentFieldOptions. - _contentFieldTypes = contentFields.Select(cf => cf.GetType()) - .Union(contentOptions.Value.ContentFieldOptions.Select(cfo => cfo.Type)); + // TODO: This code can be removed in a future release and rationalized to only use ContentPartOptions. + _contentPartTypes = contentParts.Select(cp => cp.GetType()) + .Union(contentOptions.Value.ContentPartOptions.Select(cpo => cpo.Type)); - _logger = logger; - S = localizer; - } + // TODO: This code can be removed in a future release and rationalized to only use ContentFieldOptions. + _contentFieldTypes = contentFields.Select(cf => cf.GetType()) + .Union(contentOptions.Value.ContentFieldOptions.Select(cfo => cfo.Type)); - public async Task> LoadTypesAsync() - => (await _contentDefinitionManager.LoadTypeDefinitionsAsync()) - .Select(ctd => new EditTypeViewModel(ctd)) - .OrderBy(m => m.DisplayName); + _logger = logger; + S = localizer; + } - public async Task> GetTypesAsync() - => (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Select(ctd => new EditTypeViewModel(ctd)) - .OrderBy(m => m.DisplayName); + public async Task> LoadTypesAsync() + => (await _contentDefinitionManager.LoadTypeDefinitionsAsync()) + .Select(ctd => new EditTypeViewModel(ctd)) + .OrderBy(m => m.DisplayName); - public async Task LoadTypeAsync(string name) - { - var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); + public async Task> GetTypesAsync() + => (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Select(ctd => new EditTypeViewModel(ctd)) + .OrderBy(m => m.DisplayName); - if (contentTypeDefinition == null) - { - return null; - } + public async Task LoadTypeAsync(string name) + { + var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); - return new EditTypeViewModel(contentTypeDefinition); + if (contentTypeDefinition == null) + { + return null; } - public async Task GetTypeAsync(string name) - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(name); + return new EditTypeViewModel(contentTypeDefinition); + } - if (contentTypeDefinition == null) - { - return null; - } + public async Task GetTypeAsync(string name) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(name); - return new EditTypeViewModel(contentTypeDefinition); + if (contentTypeDefinition == null) + { + return null; } - public async Task AddTypeAsync(string name, string displayName) + return new EditTypeViewModel(contentTypeDefinition); + } + + public async Task AddTypeAsync(string name, string displayName) + { + if (string.IsNullOrWhiteSpace(displayName)) { - if (string.IsNullOrWhiteSpace(displayName)) - { - throw new ArgumentException($"The '{nameof(displayName)}' can't be null or empty.", nameof(displayName)); - } + throw new ArgumentException($"The '{nameof(displayName)}' can't be null or empty.", nameof(displayName)); + } - if (string.IsNullOrWhiteSpace(name)) + if (string.IsNullOrWhiteSpace(name)) + { + name = await GenerateContentTypeNameFromDisplayNameAsync(displayName); + } + else + { + if (!name[0].IsLetter()) { - name = await GenerateContentTypeNameFromDisplayNameAsync(displayName); + throw new ArgumentException("Content type name must start with a letter", nameof(name)); } - else + if (!string.Equals(name, name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) { - if (!name[0].IsLetter()) - { - throw new ArgumentException("Content type name must start with a letter", nameof(name)); - } - if (!string.Equals(name, name.ToSafeName(), StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("Content type name contains invalid characters", nameof(name)); - } + throw new ArgumentException("Content type name contains invalid characters", nameof(name)); } + } - while (await _contentDefinitionManager.LoadTypeDefinitionAsync(name) is not null) - { - name = VersionName(name); - } + while (await _contentDefinitionManager.LoadTypeDefinitionAsync(name) is not null) + { + name = VersionName(name); + } - var contentTypeDefinition = new ContentTypeDefinition(name, displayName); + var contentTypeDefinition = new ContentTypeDefinition(name, displayName); - await _contentDefinitionManager.StoreTypeDefinitionAsync(contentTypeDefinition); + await _contentDefinitionManager.StoreTypeDefinitionAsync(contentTypeDefinition); - // Ensure it has its own part. - await _contentDefinitionManager.AlterTypeDefinitionAsync(name, builder => builder.WithPart(name)); - await _contentDefinitionManager.AlterTypeDefinitionAsync(name, cfg => cfg.Creatable().Draftable().Versionable().Listable().Securable()); + // Ensure it has its own part. + await _contentDefinitionManager.AlterTypeDefinitionAsync(name, builder => builder.WithPart(name)); + await _contentDefinitionManager.AlterTypeDefinitionAsync(name, cfg => cfg.Creatable().Draftable().Versionable().Listable().Securable()); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeCreated(context), new ContentTypeCreatedContext { ContentTypeDefinition = contentTypeDefinition }, _logger); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeCreated(context), new ContentTypeCreatedContext { ContentTypeDefinition = contentTypeDefinition }, _logger); - return contentTypeDefinition; - } + return contentTypeDefinition; + } - public async Task RemoveTypeAsync(string name, bool deleteContent) + public async Task RemoveTypeAsync(string name, bool deleteContent) + { + // First remove all attached parts. + var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); + var partDefinitions = typeDefinition.Parts.ToList(); + foreach (var partDefinition in partDefinitions) { - // First remove all attached parts. - var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); - var partDefinitions = typeDefinition.Parts.ToList(); - foreach (var partDefinition in partDefinitions) - { - await RemovePartFromTypeAsync(partDefinition.PartDefinition.Name, name); + await RemovePartFromTypeAsync(partDefinition.PartDefinition.Name, name); - // Delete the part if it's its own part. - if (partDefinition.PartDefinition.Name == name) - { - await RemovePartAsync(name); - } + // Delete the part if it's its own part. + if (partDefinition.PartDefinition.Name == name) + { + await RemovePartAsync(name); } + } - await _contentDefinitionManager.DeleteTypeDefinitionAsync(name); + await _contentDefinitionManager.DeleteTypeDefinitionAsync(name); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeRemoved(context), new ContentTypeRemovedContext { ContentTypeDefinition = typeDefinition }, _logger); - } + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeRemoved(context), new ContentTypeRemovedContext { ContentTypeDefinition = typeDefinition }, _logger); + } - public async Task AddPartToTypeAsync(string partName, string typeName) - { - await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder.WithPart(partName)); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartAttached(context), new ContentPartAttachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); - } + public async Task AddPartToTypeAsync(string partName, string typeName) + { + await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder.WithPart(partName)); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartAttached(context), new ContentPartAttachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); + } - public async Task AddReusablePartToTypeAsync(string name, string displayName, string description, string partName, string typeName) - { - await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder - .WithPart(name, partName, partBuilder => - { - partBuilder.WithDisplayName(displayName); - partBuilder.WithDescription(description); - }) - ); + public async Task AddReusablePartToTypeAsync(string name, string displayName, string description, string partName, string typeName) + { + await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder + .WithPart(name, partName, partBuilder => + { + partBuilder.WithDisplayName(displayName); + partBuilder.WithDescription(description); + }) + ); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartAttached(context), new ContentPartAttachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); - } + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartAttached(context), new ContentPartAttachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); + } - public async Task RemovePartFromTypeAsync(string partName, string typeName) - { - await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder.RemovePart(partName)); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartDetached(context), new ContentPartDetachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); - } + public async Task RemovePartFromTypeAsync(string partName, string typeName) + { + await _contentDefinitionManager.AlterTypeDefinitionAsync(typeName, typeBuilder => typeBuilder.RemovePart(partName)); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartDetached(context), new ContentPartDetachedContext { ContentTypeName = typeName, ContentPartName = partName }, _logger); + } - public async Task> LoadPartsAsync(bool metadataPartsOnly) - { - var typeNames = new HashSet((await LoadTypesAsync()).Select(ctd => ctd.Name)); - - // User-defined parts. - // Except for those parts with the same name as a type (implicit type's part or a mistake). - var userContentParts = (await _contentDefinitionManager.LoadPartDefinitionsAsync()) - .Where(cpd => !typeNames.Contains(cpd.Name)) - .Select(cpd => new EditPartViewModel(cpd)) - .ToDictionary(k => k.Name); - - // Code-defined parts. - var codeDefinedParts = metadataPartsOnly - ? [] - : _contentPartTypes - .Where(cpd => !userContentParts.ContainsKey(cpd.Name)) - .Select(cpi => new EditPartViewModel { Name = cpi.Name, DisplayName = cpi.Name }) - .ToList(); - - // Order by display name. - return codeDefinedParts - .Union(userContentParts.Values) - .OrderBy(m => m.DisplayName); - } + public async Task> LoadPartsAsync(bool metadataPartsOnly) + { + var typeNames = new HashSet((await LoadTypesAsync()).Select(ctd => ctd.Name)); + + // User-defined parts. + // Except for those parts with the same name as a type (implicit type's part or a mistake). + var userContentParts = (await _contentDefinitionManager.LoadPartDefinitionsAsync()) + .Where(cpd => !typeNames.Contains(cpd.Name)) + .Select(cpd => new EditPartViewModel(cpd)) + .ToDictionary(k => k.Name); + + // Code-defined parts. + var codeDefinedParts = metadataPartsOnly + ? [] + : _contentPartTypes + .Where(cpd => !userContentParts.ContainsKey(cpd.Name)) + .Select(cpi => new EditPartViewModel { Name = cpi.Name, DisplayName = cpi.Name }) + .ToList(); + + // Order by display name. + return codeDefinedParts + .Union(userContentParts.Values) + .OrderBy(m => m.DisplayName); + } - public async Task> GetPartsAsync(bool metadataPartsOnly) - { - var typeNames = new HashSet((await GetTypesAsync()).Select(ctd => ctd.Name)); - - // User-defined parts. - // Except for those parts with the same name as a type (implicit type's part or a mistake). - var userContentParts = (await _contentDefinitionManager.ListPartDefinitionsAsync()) - .Where(cpd => !typeNames.Contains(cpd.Name)) - .Select(cpd => new EditPartViewModel(cpd)) - .ToDictionary(k => k.Name); - - // Code-defined parts. - var codeDefinedParts = metadataPartsOnly - ? [] - : _contentPartTypes - .Where(cpd => !userContentParts.ContainsKey(cpd.Name)) - .Select(cpi => new EditPartViewModel { Name = cpi.Name, DisplayName = cpi.Name }) - .ToList(); - - // Order by display name. - return codeDefinedParts - .Union(userContentParts.Values) - .OrderBy(m => m.DisplayName); - } + public async Task> GetPartsAsync(bool metadataPartsOnly) + { + var typeNames = new HashSet((await GetTypesAsync()).Select(ctd => ctd.Name)); + + // User-defined parts. + // Except for those parts with the same name as a type (implicit type's part or a mistake). + var userContentParts = (await _contentDefinitionManager.ListPartDefinitionsAsync()) + .Where(cpd => !typeNames.Contains(cpd.Name)) + .Select(cpd => new EditPartViewModel(cpd)) + .ToDictionary(k => k.Name); + + // Code-defined parts. + var codeDefinedParts = metadataPartsOnly + ? [] + : _contentPartTypes + .Where(cpd => !userContentParts.ContainsKey(cpd.Name)) + .Select(cpi => new EditPartViewModel { Name = cpi.Name, DisplayName = cpi.Name }) + .ToList(); + + // Order by display name. + return codeDefinedParts + .Union(userContentParts.Values) + .OrderBy(m => m.DisplayName); + } - public async Task LoadPartAsync(string name) + public async Task LoadPartAsync(string name) + { + var contentPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(name); + + if (contentPartDefinition == null) { - var contentPartDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(name); + var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); - if (contentPartDefinition == null) + if (contentTypeDefinition == null) { - var contentTypeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(name); + return null; + } - if (contentTypeDefinition == null) - { - return null; - } + contentPartDefinition = new ContentPartDefinition(name); + } - contentPartDefinition = new ContentPartDefinition(name); - } + var viewModel = new EditPartViewModel(contentPartDefinition); - var viewModel = new EditPartViewModel(contentPartDefinition); + return viewModel; + } - return viewModel; - } + public async Task GetPartAsync(string name) + { + var contentPartDefinition = await _contentDefinitionManager.GetPartDefinitionAsync(name); - public async Task GetPartAsync(string name) + if (contentPartDefinition == null) { - var contentPartDefinition = await _contentDefinitionManager.GetPartDefinitionAsync(name); + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(name); - if (contentPartDefinition == null) + if (contentTypeDefinition == null) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(name); - - if (contentTypeDefinition == null) - { - return null; - } - - contentPartDefinition = new ContentPartDefinition(name); + return null; } - var viewModel = new EditPartViewModel(contentPartDefinition); - - return viewModel; + contentPartDefinition = new ContentPartDefinition(name); } - public async Task AddPartAsync(CreatePartViewModel partViewModel) - { - var name = partViewModel.Name; + var viewModel = new EditPartViewModel(contentPartDefinition); - if (await _contentDefinitionManager.LoadPartDefinitionAsync(name) is not null) - { - throw new Exception(S["Cannot add part named '{0}'. It already exists.", name]); - } - - if (!string.IsNullOrEmpty(name)) - { - await _contentDefinitionManager.AlterPartDefinitionAsync(name, builder => builder.Attachable()); - var partDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(name); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartCreated(context), new ContentPartCreatedContext { ContentPartDefinition = partDefinition }, _logger); + return viewModel; + } - return new EditPartViewModel(partDefinition); - } + public async Task AddPartAsync(CreatePartViewModel partViewModel) + { + var name = partViewModel.Name; - return null; + if (await _contentDefinitionManager.LoadPartDefinitionAsync(name) is not null) + { + throw new Exception(S["Cannot add part named '{0}'. It already exists.", name]); } - public async Task RemovePartAsync(string name) + if (!string.IsNullOrEmpty(name)) { + await _contentDefinitionManager.AlterPartDefinitionAsync(name, builder => builder.Attachable()); var partDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(name); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartCreated(context), new ContentPartCreatedContext { ContentPartDefinition = partDefinition }, _logger); - if (partDefinition == null) - { - // Couldn't find this named part, ignore it. - return; - } - - foreach (var fieldDefinition in partDefinition.Fields) - { - await RemoveFieldFromPartAsync(fieldDefinition.Name, name); - } - - await _contentDefinitionManager.DeletePartDefinitionAsync(name); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartRemoved(context), new ContentPartRemovedContext { ContentPartDefinition = partDefinition }, _logger); + return new EditPartViewModel(partDefinition); } - public Task> GetFieldsAsync() - => Task.FromResult(_contentFieldTypes); + return null; + } - public Task AddFieldToPartAsync(string fieldName, string fieldTypeName, string partName) - => AddFieldToPartAsync(fieldName, fieldName, fieldTypeName, partName); + public async Task RemovePartAsync(string name) + { + var partDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(name); - public async Task AddFieldToPartAsync(string fieldName, string displayName, string fieldTypeName, string partName) + if (partDefinition == null) { - if (string.IsNullOrEmpty(fieldName)) - { - throw new ArgumentException($"The '{nameof(fieldName)}' can't be null or empty.", nameof(fieldName)); - } + // Couldn't find this named part, ignore it. + return; + } - var partDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(partName); - var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(partName); + foreach (var fieldDefinition in partDefinition.Fields) + { + await RemoveFieldFromPartAsync(fieldDefinition.Name, name); + } - // If the type exists ensure it has its own part. - if (typeDefinition != null) - { - await _contentDefinitionManager.AlterTypeDefinitionAsync(partName, builder => builder.WithPart(partName)); - } + await _contentDefinitionManager.DeletePartDefinitionAsync(name); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartRemoved(context), new ContentPartRemovedContext { ContentPartDefinition = partDefinition }, _logger); + } - fieldName = fieldName.ToSafeName(); + public Task> GetFieldsAsync() + => Task.FromResult(_contentFieldTypes); - await _contentDefinitionManager.AlterPartDefinitionAsync(partName, - partBuilder => partBuilder.WithField(fieldName, fieldBuilder => fieldBuilder.OfType(fieldTypeName).WithDisplayName(displayName))); + public Task AddFieldToPartAsync(string fieldName, string fieldTypeName, string partName) + => AddFieldToPartAsync(fieldName, fieldName, fieldTypeName, partName); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentFieldAttached(context), new ContentFieldAttachedContext - { - ContentPartName = partName, - ContentFieldTypeName = fieldTypeName, - ContentFieldName = fieldName, - ContentFieldDisplayName = displayName - }, _logger); + public async Task AddFieldToPartAsync(string fieldName, string displayName, string fieldTypeName, string partName) + { + if (string.IsNullOrEmpty(fieldName)) + { + throw new ArgumentException($"The '{nameof(fieldName)}' can't be null or empty.", nameof(fieldName)); } - public async Task RemoveFieldFromPartAsync(string fieldName, string partName) + var partDefinition = await _contentDefinitionManager.LoadPartDefinitionAsync(partName); + var typeDefinition = await _contentDefinitionManager.LoadTypeDefinitionAsync(partName); + + // If the type exists ensure it has its own part. + if (typeDefinition != null) { - await _contentDefinitionManager.AlterPartDefinitionAsync(partName, typeBuilder => typeBuilder.RemoveField(fieldName)); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentFieldDetached(context), new ContentFieldDetachedContext - { - ContentPartName = partName, - ContentFieldName = fieldName - }, _logger); + await _contentDefinitionManager.AlterTypeDefinitionAsync(partName, builder => builder.WithPart(partName)); } - public async Task AlterFieldAsync(EditPartViewModel partViewModel, EditFieldViewModel fieldViewModel) - { - await _contentDefinitionManager.AlterPartDefinitionAsync(partViewModel.Name, partBuilder => - { - partBuilder.WithField(fieldViewModel.Name, fieldBuilder => - { - fieldBuilder.WithDisplayName(fieldViewModel.DisplayName); - fieldBuilder.WithEditor(fieldViewModel.Editor); - fieldBuilder.WithDisplayMode(fieldViewModel.DisplayMode); - }); - }); + fieldName = fieldName.ToSafeName(); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartFieldUpdated(context), new ContentPartFieldUpdatedContext - { - ContentPartName = partViewModel.Name, - ContentFieldName = fieldViewModel.Name - }, _logger); - } + await _contentDefinitionManager.AlterPartDefinitionAsync(partName, + partBuilder => partBuilder.WithField(fieldName, fieldBuilder => fieldBuilder.OfType(fieldTypeName).WithDisplayName(displayName))); - public async Task AlterTypePartAsync(EditTypePartViewModel typePartViewModel) + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentFieldAttached(context), new ContentFieldAttachedContext { - var typeDefinition = typePartViewModel.TypePartDefinition.ContentTypeDefinition; - - await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type => - { - type.WithPart(typePartViewModel.Name, typePartViewModel.TypePartDefinition.PartDefinition, part => - { - part.WithDisplayName(typePartViewModel.DisplayName); - part.WithDescription(typePartViewModel.Description); - part.WithEditor(typePartViewModel.Editor); - part.WithDisplayMode(typePartViewModel.DisplayMode); - }); - }); + ContentPartName = partName, + ContentFieldTypeName = fieldTypeName, + ContentFieldName = fieldName, + ContentFieldDisplayName = displayName + }, _logger); + } - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypePartUpdated(context), new ContentTypePartUpdatedContext - { - ContentTypeName = typeDefinition.Name, - ContentPartName = typePartViewModel.Name - }, _logger); - } + public async Task RemoveFieldFromPartAsync(string fieldName, string partName) + { + await _contentDefinitionManager.AlterPartDefinitionAsync(partName, typeBuilder => typeBuilder.RemoveField(fieldName)); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentFieldDetached(context), new ContentFieldDetachedContext + { + ContentPartName = partName, + ContentFieldName = fieldName + }, _logger); + } - public async Task AlterTypePartsOrderAsync(ContentTypeDefinition typeDefinition, string[] partNames) + public async Task AlterFieldAsync(EditPartViewModel partViewModel, EditFieldViewModel fieldViewModel) + { + await _contentDefinitionManager.AlterPartDefinitionAsync(partViewModel.Name, partBuilder => { - await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type => + partBuilder.WithField(fieldViewModel.Name, fieldBuilder => { - if (partNames is null) - { - return; - } + fieldBuilder.WithDisplayName(fieldViewModel.DisplayName); + fieldBuilder.WithEditor(fieldViewModel.Editor); + fieldBuilder.WithDisplayMode(fieldViewModel.DisplayMode); + }); + }); - for (var i = 0; i < partNames.Length; i++) - { - var partDefinition = typeDefinition.Parts?.FirstOrDefault(x => x.Name == partNames[i]); + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartFieldUpdated(context), new ContentPartFieldUpdatedContext + { + ContentPartName = partViewModel.Name, + ContentFieldName = fieldViewModel.Name + }, _logger); + } - if (partDefinition == null) - { - continue; - } + public async Task AlterTypePartAsync(EditTypePartViewModel typePartViewModel) + { + var typeDefinition = typePartViewModel.TypePartDefinition.ContentTypeDefinition; - type.WithPart(partNames[i], partDefinition.PartDefinition, part => - { - part.MergeSettings(x => x.Position = i.ToString()); - }); - } + await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type => + { + type.WithPart(typePartViewModel.Name, typePartViewModel.TypePartDefinition.PartDefinition, part => + { + part.WithDisplayName(typePartViewModel.DisplayName); + part.WithDescription(typePartViewModel.Description); + part.WithEditor(typePartViewModel.Editor); + part.WithDisplayMode(typePartViewModel.DisplayMode); }); + }); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeUpdated(context), new ContentTypeUpdatedContext - { - ContentTypeDefinition = typeDefinition - }, _logger); - } + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypePartUpdated(context), new ContentTypePartUpdatedContext + { + ContentTypeName = typeDefinition.Name, + ContentPartName = typePartViewModel.Name + }, _logger); + } - public async Task AlterPartFieldsOrderAsync(ContentPartDefinition partDefinition, string[] fieldNames) + public async Task AlterTypePartsOrderAsync(ContentTypeDefinition typeDefinition, string[] partNames) + { + await _contentDefinitionManager.AlterTypeDefinitionAsync(typeDefinition.Name, type => { - await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, type => + if (partNames is null) + { + return; + } + + for (var i = 0; i < partNames.Length; i++) { - if (fieldNames is null) + var partDefinition = typeDefinition.Parts?.FirstOrDefault(x => x.Name == partNames[i]); + + if (partDefinition == null) { - return; + continue; } - for (var i = 0; i < fieldNames.Length; i++) + type.WithPart(partNames[i], partDefinition.PartDefinition, part => { - var fieldDefinition = partDefinition.Fields.FirstOrDefault(x => x.Name == fieldNames[i]); - type.WithField(fieldNames[i], field => - { - field.MergeSettings(x => x.Position = i.ToString()); - }); - } - }); + part.MergeSettings(x => x.Position = i.ToString()); + }); + } + }); - _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartUpdated(context), new ContentPartUpdatedContext - { - ContentPartDefinition = partDefinition - }, _logger); - } + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentTypeUpdated(context), new ContentTypeUpdatedContext + { + ContentTypeDefinition = typeDefinition + }, _logger); + } - public async Task GenerateContentTypeNameFromDisplayNameAsync(string displayName) + public async Task AlterPartFieldsOrderAsync(ContentPartDefinition partDefinition, string[] fieldNames) + { + await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, type => { - displayName = displayName.ToSafeName(); + if (fieldNames is null) + { + return; + } - while (await _contentDefinitionManager.LoadTypeDefinitionAsync(displayName) != null) + for (var i = 0; i < fieldNames.Length; i++) { - displayName = VersionName(displayName); + var fieldDefinition = partDefinition.Fields.FirstOrDefault(x => x.Name == fieldNames[i]); + type.WithField(fieldNames[i], field => + { + field.MergeSettings(x => x.Position = i.ToString()); + }); } + }); - return displayName; - } + _contentDefinitionEventHandlers.Invoke((handler, context) => handler.ContentPartUpdated(context), new ContentPartUpdatedContext + { + ContentPartDefinition = partDefinition + }, _logger); + } - public async Task GenerateFieldNameFromDisplayNameAsync(string partName, string displayName) + public async Task GenerateContentTypeNameFromDisplayNameAsync(string displayName) + { + displayName = displayName.ToSafeName(); + + while (await _contentDefinitionManager.LoadTypeDefinitionAsync(displayName) != null) { - IEnumerable fieldDefinitions; + displayName = VersionName(displayName); + } - var part = await _contentDefinitionManager.LoadPartDefinitionAsync(partName); - displayName = displayName.ToSafeName(); + return displayName; + } - if (part == null) - { - var type = await _contentDefinitionManager.LoadTypeDefinitionAsync(partName) - ?? throw new ArgumentException("The part doesn't exist: " + partName); + public async Task GenerateFieldNameFromDisplayNameAsync(string partName, string displayName) + { + IEnumerable fieldDefinitions; - var typePart = type.Parts?.FirstOrDefault(x => x.PartDefinition.Name == partName); + var part = await _contentDefinitionManager.LoadPartDefinitionAsync(partName); + displayName = displayName.ToSafeName(); - // If passed in might be that of a type w/ no implicit field. - if (typePart == null) - { - return displayName; - } + if (part == null) + { + var type = await _contentDefinitionManager.LoadTypeDefinitionAsync(partName) + ?? throw new ArgumentException("The part doesn't exist: " + partName); - fieldDefinitions = typePart.PartDefinition.Fields.ToList(); - } - else - { - fieldDefinitions = part.Fields.ToList(); - } + var typePart = type.Parts?.FirstOrDefault(x => x.PartDefinition.Name == partName); - while (fieldDefinitions.Any(x => string.Equals(displayName.Trim(), x.Name.Trim(), StringComparison.OrdinalIgnoreCase))) + // If passed in might be that of a type w/ no implicit field. + if (typePart == null) { - displayName = VersionName(displayName); + return displayName; } - return displayName; + fieldDefinitions = typePart.PartDefinition.Fields.ToList(); + } + else + { + fieldDefinitions = part.Fields.ToList(); } - private static string VersionName(string name) + while (fieldDefinitions.Any(x => string.Equals(displayName.Trim(), x.Name.Trim(), StringComparison.OrdinalIgnoreCase))) { - int version; - var nameParts = name.Split('-', StringSplitOptions.RemoveEmptyEntries); + displayName = VersionName(displayName); + } - if (nameParts.Length > 1 && int.TryParse(nameParts.Last(), out version)) - { - version = version > 0 ? ++version : 2; + return displayName; + } - // This could unintentionally chomp something that looks like a version. - name = string.Join('-', nameParts.Take(nameParts.Length - 1)); - } - else - { - version = 2; - } + private static string VersionName(string name) + { + int version; + var nameParts = name.Split('-', StringSplitOptions.RemoveEmptyEntries); + + if (nameParts.Length > 1 && int.TryParse(nameParts.Last(), out version)) + { + version = version > 0 ? ++version : 2; - return string.Format("{0}-{1}", name, version); + // This could unintentionally chomp something that looks like a version. + name = string.Join('-', nameParts.Take(nameParts.Length - 1)); } + else + { + version = 2; + } + + return string.Format("{0}-{1}", name, version); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/StereotypeService.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/StereotypeService.cs index 11d69b203b5..68fd82abffe 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/StereotypeService.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Services/StereotypeService.cs @@ -7,47 +7,46 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Modules; -namespace OrchardCore.ContentTypes.Services +namespace OrchardCore.ContentTypes.Services; + +public class StereotypeService : IStereotypeService { - public class StereotypeService : IStereotypeService + private readonly IEnumerable _providers; + private readonly IContentDefinitionService _contentDefinitionService; + private readonly ILogger _logger; + + public StereotypeService( + IEnumerable providers, + IContentDefinitionService contentDefinitionService, + ILogger logger) { - private readonly IEnumerable _providers; - private readonly IContentDefinitionService _contentDefinitionService; - private readonly ILogger _logger; - - public StereotypeService( - IEnumerable providers, - IContentDefinitionService contentDefinitionService, - ILogger logger) - { - _providers = providers; - _contentDefinitionService = contentDefinitionService; - _logger = logger; - } + _providers = providers; + _contentDefinitionService = contentDefinitionService; + _logger = logger; + } - public async Task> GetStereotypesAsync() - { - var providerStereotypes = (await _providers.InvokeAsync(provider => provider.GetStereotypesAsync(), _logger)).ToList(); + public async Task> GetStereotypesAsync() + { + var providerStereotypes = (await _providers.InvokeAsync(provider => provider.GetStereotypesAsync(), _logger)).ToList(); - var stereotypes = providerStereotypes.Select(providerStereotype => providerStereotype.Stereotype) - .ToHashSet(StringComparer.OrdinalIgnoreCase); + var stereotypes = providerStereotypes.Select(providerStereotype => providerStereotype.Stereotype) + .ToHashSet(StringComparer.OrdinalIgnoreCase); - foreach (var contentType in await _contentDefinitionService.GetTypesAsync()) + foreach (var contentType in await _contentDefinitionService.GetTypesAsync()) + { + if (!contentType.TypeDefinition.TryGetStereotype(out var stereotype) || + stereotypes.Contains(stereotype)) { - if (!contentType.TypeDefinition.TryGetStereotype(out var stereotype) || - stereotypes.Contains(stereotype)) - { - continue; - } - - providerStereotypes.Add(new StereotypeDescription - { - Stereotype = stereotype, - DisplayName = stereotype - }); + continue; } - return providerStereotypes.OrderBy(x => x.DisplayName); + providerStereotypes.Add(new StereotypeDescription + { + Stereotype = stereotype, + DisplayName = stereotype + }); } + + return providerStereotypes.OrderBy(x => x.DisplayName); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Startup.cs index d3121ff3018..f56d7e70bbb 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/Startup.cs @@ -13,42 +13,41 @@ using OrchardCore.ResourceManagement; using OrchardCore.Security.Permissions; -namespace OrchardCore.ContentTypes +namespace OrchardCore.ContentTypes; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // TODO: Put in its own feature to be able to execute this recipe without having to enable - // Content Types management UI - services.AddRecipeExecutionStep(); - services.AddRecipeExecutionStep(); - services.AddRecipeExecutionStep(); + // TODO: Put in its own feature to be able to execute this recipe without having to enable + // Content Types management UI + services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.AddTransient(); - services.AddTransient, ResourceManagementOptionsConfiguration>(); - } + services.AddTransient(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddDeployment(); - services.AddDeployment(); - } + services.AddDeployment(); + services.AddDeployment(); + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddFieldViewModel.cs index f000464b507..72f230fdc9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddFieldViewModel.cs @@ -3,41 +3,40 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class AddFieldViewModel { - public class AddFieldViewModel + public AddFieldViewModel() { - public AddFieldViewModel() - { - Fields = []; - } + Fields = []; + } - /// - /// The technical name of the field. - /// - public string Name { get; set; } + /// + /// The technical name of the field. + /// + public string Name { get; set; } - /// - /// The display name of the field. - /// - public string DisplayName { get; set; } + /// + /// The display name of the field. + /// + public string DisplayName { get; set; } - /// - /// The selected field type. - /// - [Required] - public string FieldTypeName { get; set; } + /// + /// The selected field type. + /// + [Required] + public string FieldTypeName { get; set; } - /// - /// The part to add the field to. - /// - [BindNever] - public ContentPartDefinition Part { get; set; } + /// + /// The part to add the field to. + /// + [BindNever] + public ContentPartDefinition Part { get; set; } - /// - /// List of the available Field types. - /// - [BindNever] - public List Fields { get; set; } - } + /// + /// List of the available Field types. + /// + [BindNever] + public List Fields { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddPartsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddPartsViewModel.cs index 7186c20d1f1..b2b98f99d53 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddPartsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/AddPartsViewModel.cs @@ -1,38 +1,37 @@ using System.Collections.Generic; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class AddPartsViewModel { - public class AddPartsViewModel + public AddPartsViewModel() { - public AddPartsViewModel() - { - PartSelections = []; - } - - public EditTypeViewModel Type { get; set; } - public List PartSelections { get; set; } + PartSelections = []; } - public class AddReusablePartViewModel - { - public AddReusablePartViewModel() - { - PartSelections = []; - } - - public EditTypeViewModel Type { get; set; } - public string Name { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public List PartSelections { get; set; } - public string SelectedPartName { get; set; } - } + public EditTypeViewModel Type { get; set; } + public List PartSelections { get; set; } +} - public class PartSelectionViewModel +public class AddReusablePartViewModel +{ + public AddReusablePartViewModel() { - public string PartName { get; set; } - public string PartDisplayName { get; set; } - public string PartDescription { get; set; } - public bool IsSelected { get; set; } + PartSelections = []; } + + public EditTypeViewModel Type { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public List PartSelections { get; set; } + public string SelectedPartName { get; set; } +} + +public class PartSelectionViewModel +{ + public string PartName { get; set; } + public string PartDisplayName { get; set; } + public string PartDescription { get; set; } + public bool IsSelected { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentDefinitionStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentDefinitionStepViewModel.cs index bf5604b4b15..d8d5868a272 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentDefinitionStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentDefinitionStepViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ContentDefinitionStepViewModel { - public class ContentDefinitionStepViewModel - { - public string[] ContentTypes { get; set; } + public string[] ContentTypes { get; set; } - public string[] ContentParts { get; set; } + public string[] ContentParts { get; set; } - public bool IncludeAll { get; internal set; } - } + public bool IncludeAll { get; internal set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentPartSettingsViewModel.cs index 619447e54f9..11d03e796d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentPartSettingsViewModel.cs @@ -1,16 +1,15 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ContentPartSettingsViewModel { - public class ContentPartSettingsViewModel - { - public bool Attachable { get; set; } - public bool Reusable { get; set; } - public string Description { get; set; } - public string DisplayName { get; set; } + public bool Attachable { get; set; } + public bool Reusable { get; set; } + public string Description { get; set; } + public string DisplayName { get; set; } - [BindNever] - public ContentPartDefinition ContentPartDefinition { get; set; } - } + [BindNever] + public ContentPartDefinition ContentPartDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs index 3081711a4d7..a7a34445c1b 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeSettingsViewModel.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ContentTypeSettingsViewModel { - public class ContentTypeSettingsViewModel - { - public bool Creatable { get; set; } + public bool Creatable { get; set; } - public bool Listable { get; set; } + public bool Listable { get; set; } - public bool Draftable { get; set; } + public bool Draftable { get; set; } - public bool Versionable { get; set; } + public bool Versionable { get; set; } - public bool Securable { get; set; } + public bool Securable { get; set; } - public string Stereotype { get; set; } + public string Stereotype { get; set; } - public string Description { get; set; } + public string Description { get; set; } - [BindNever] - public ContentTypeDefinitionDriverOptions Options { get; set; } - } + [BindNever] + public ContentTypeDefinitionDriverOptions Options { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeViewModel.cs index 7fa573c526f..939f4cd4840 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ContentTypeViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ContentTypeViewModel { - public class ContentTypeViewModel - { - public string DisplayName { get; set; } - public string Name { get; set; } - } + public string DisplayName { get; set; } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreatePartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreatePartViewModel.cs index 08c73178b92..c8cd3e0e070 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreatePartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreatePartViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class CreatePartViewModel { - public class CreatePartViewModel - { - public string Name { get; set; } - } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreateTypeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreateTypeViewModel.cs index 344abef1f79..4c3b656a9a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreateTypeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/CreateTypeViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class CreateTypeViewModel { - public class CreateTypeViewModel - { - public string DisplayName { get; set; } - public string Name { get; set; } - } + public string DisplayName { get; set; } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/DeleteContentDefinitionStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/DeleteContentDefinitionStepViewModel.cs index 262b9a17451..fe0566bb630 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/DeleteContentDefinitionStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/DeleteContentDefinitionStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class DeleteContentDefinitionStepViewModel { - public class DeleteContentDefinitionStepViewModel - { - public string ContentTypes { get; set; } + public string ContentTypes { get; set; } - public string ContentParts { get; set; } - } + public string ContentParts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditFieldViewModel.cs index a422c081241..b3fe4640ae5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditFieldViewModel.cs @@ -2,35 +2,34 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class EditFieldViewModel { - public class EditFieldViewModel - { - /// - /// The technical name of the field. - /// - public string Name { get; set; } + /// + /// The technical name of the field. + /// + public string Name { get; set; } - /// - /// The display name of the field. - /// - [Required] - public string DisplayName { get; set; } + /// + /// The display name of the field. + /// + [Required] + public string DisplayName { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } - [BindNever] - public dynamic Shape { get; set; } + [BindNever] + public dynamic Shape { get; set; } - /// - /// The editor name of the field. - /// - public string Editor { get; set; } + /// + /// The editor name of the field. + /// + public string Editor { get; set; } - /// - /// The display mode of the field. - /// - public string DisplayMode { get; set; } - } + /// + /// The display mode of the field. + /// + public string DisplayMode { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditPartViewModel.cs index 69bce871c76..d5e55fac293 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditPartViewModel.cs @@ -4,42 +4,41 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class EditPartViewModel { - public class EditPartViewModel + public EditPartViewModel() + { + } + + public EditPartViewModel(ContentPartDefinition contentPartDefinition) + { + Name = contentPartDefinition.Name; + PartDefinition = contentPartDefinition; + _displayName = contentPartDefinition.DisplayName(); + } + + public string Name { get; set; } + + private string _displayName; + + [Required] + public string DisplayName { - public EditPartViewModel() - { - } - - public EditPartViewModel(ContentPartDefinition contentPartDefinition) - { - Name = contentPartDefinition.Name; - PartDefinition = contentPartDefinition; - _displayName = contentPartDefinition.DisplayName(); - } - - public string Name { get; set; } - - private string _displayName; - - [Required] - public string DisplayName - { - get { return !string.IsNullOrWhiteSpace(_displayName) ? _displayName : Name.TrimEnd("Part").CamelFriendly(); } - set { _displayName = value; } - } - - public string Description - { - get { return PartDefinition.GetSettings().Description; } - set { PartDefinition.GetSettings().Description = value; } - } - - [BindNever] - public ContentPartDefinition PartDefinition { get; private set; } - - [BindNever] - public dynamic Editor { get; set; } + get { return !string.IsNullOrWhiteSpace(_displayName) ? _displayName : Name.TrimEnd("Part").CamelFriendly(); } + set { _displayName = value; } } + + public string Description + { + get { return PartDefinition.GetSettings().Description; } + set { PartDefinition.GetSettings().Description = value; } + } + + [BindNever] + public ContentPartDefinition PartDefinition { get; private set; } + + [BindNever] + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypePartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypePartViewModel.cs index ae731fce941..4164bb88c62 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypePartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypePartViewModel.cs @@ -1,36 +1,35 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class EditTypePartViewModel { - public class EditTypePartViewModel - { - /// - /// The technical name of the part. - /// - public string Name { get; set; } - - /// - /// The display name of the part. - /// - public string DisplayName { get; set; } - - public string Description { get; set; } - - [BindNever] - public ContentTypePartDefinition TypePartDefinition { get; set; } - - [BindNever] - public dynamic Shape { get; set; } - - /// - /// The editor name of the part. - /// - public string Editor { get; set; } - - /// - /// The display mode of the part. - /// - public string DisplayMode { get; set; } - } + /// + /// The technical name of the part. + /// + public string Name { get; set; } + + /// + /// The display name of the part. + /// + public string DisplayName { get; set; } + + public string Description { get; set; } + + [BindNever] + public ContentTypePartDefinition TypePartDefinition { get; set; } + + [BindNever] + public dynamic Shape { get; set; } + + /// + /// The editor name of the part. + /// + public string Editor { get; set; } + + /// + /// The display mode of the part. + /// + public string DisplayMode { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypeViewModel.cs index 8097a9367b8..91979f07bc1 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/EditTypeViewModel.cs @@ -2,35 +2,34 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class EditTypeViewModel { - public class EditTypeViewModel + public EditTypeViewModel() { - public EditTypeViewModel() - { - Settings = []; - } + Settings = []; + } - public EditTypeViewModel(ContentTypeDefinition contentTypeDefinition) - { - Name = contentTypeDefinition.Name; - DisplayName = contentTypeDefinition.DisplayName; - Settings = contentTypeDefinition.Settings; - TypeDefinition = contentTypeDefinition; - } + public EditTypeViewModel(ContentTypeDefinition contentTypeDefinition) + { + Name = contentTypeDefinition.Name; + DisplayName = contentTypeDefinition.DisplayName; + Settings = contentTypeDefinition.Settings; + TypeDefinition = contentTypeDefinition; + } - public string Name { get; set; } - public string DisplayName { get; set; } - public string[] OrderedFieldNames { get; set; } - public string[] OrderedPartNames { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public string[] OrderedFieldNames { get; set; } + public string[] OrderedPartNames { get; set; } - [BindNever] - public JsonObject Settings { get; set; } + [BindNever] + public JsonObject Settings { get; set; } - [BindNever] - public ContentTypeDefinition TypeDefinition { get; set; } + [BindNever] + public ContentTypeDefinition TypeDefinition { get; set; } - [BindNever] - public dynamic Editor { get; set; } - } + [BindNever] + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentPartsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentPartsViewModel.cs index e2935066038..60267fd83a5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentPartsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentPartsViewModel.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ListContentPartsViewModel { - public class ListContentPartsViewModel - { - [BindNever] - public IEnumerable Parts { get; set; } - } + [BindNever] + public IEnumerable Parts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentTypesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentTypesViewModel.cs index e8529a942c5..29a5e396453 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentTypesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ListContentTypesViewModel.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ListContentTypesViewModel { - public class ListContentTypesViewModel - { - [BindNever] - public IEnumerable Types { get; set; } - } + [BindNever] + public IEnumerable Types { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ReplaceContentDefinitionStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ReplaceContentDefinitionStepViewModel.cs index 200a2fa156e..350960440e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ReplaceContentDefinitionStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ContentTypes/ViewModels/ReplaceContentDefinitionStepViewModel.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.ContentTypes.ViewModels +namespace OrchardCore.ContentTypes.ViewModels; + +public class ReplaceContentDefinitionStepViewModel : ContentDefinitionStepViewModel { - public class ReplaceContentDefinitionStepViewModel : ContentDefinitionStepViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AdminMenu.cs index 22a7fe9401e..c22015857c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AdminMenu.cs @@ -14,104 +14,103 @@ using OrchardCore.Navigation; using OrchardCore.Settings; -namespace OrchardCore.Contents +namespace OrchardCore.Contents; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Contents" }, - { "contentTypeId", string.Empty }, - }; + { "area", "OrchardCore.Contents" }, + { "contentTypeId", string.Empty }, + }; - private static readonly RouteValueDictionary _adminListRouteValues = new() - { - { "area", "OrchardCore.Contents" }, - { "controller", "Admin" }, - { "Action", "List" }, - }; + private static readonly RouteValueDictionary _adminListRouteValues = new() + { + { "area", "OrchardCore.Contents" }, + { "controller", "Admin" }, + { "Action", "List" }, + }; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly LinkGenerator _linkGenerator; - private readonly IAuthorizationService _authorizationService; - private readonly ISiteService _siteService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly LinkGenerator _linkGenerator; + private readonly IAuthorizationService _authorizationService; + private readonly ISiteService _siteService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - IHttpContextAccessor httpContextAccessor, - LinkGenerator linkGenerator, - IAuthorizationService authorizationService, - ISiteService siteService, - IStringLocalizer localizer) - { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _httpContextAccessor = httpContextAccessor; - _linkGenerator = linkGenerator; - _authorizationService = authorizationService; - _siteService = siteService; - S = localizer; - } + public AdminMenu( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IHttpContextAccessor httpContextAccessor, + LinkGenerator linkGenerator, + IAuthorizationService authorizationService, + ISiteService siteService, + IStringLocalizer localizer) + { + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _httpContextAccessor = httpContextAccessor; + _linkGenerator = linkGenerator; + _authorizationService = authorizationService; + _siteService = siteService; + S = localizer; + } - public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return; - } + return; + } - var context = _httpContextAccessor.HttpContext; + var context = _httpContextAccessor.HttpContext; - var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); - var listableContentTypes = contentTypeDefinitions.Where(ctd => ctd.IsListable()); - await builder.AddAsync(S["Content"], NavigationConstants.AdminMenuContentPosition, async content => + var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + var listableContentTypes = contentTypeDefinitions.Where(ctd => ctd.IsListable()); + await builder.AddAsync(S["Content"], NavigationConstants.AdminMenuContentPosition, async content => + { + content.AddClass("content").Id("content"); + await content.AddAsync(S["Content Items"], S["Content Items"].PrefixPosition(), async contentItems => { - content.AddClass("content").Id("content"); - await content.AddAsync(S["Content Items"], S["Content Items"].PrefixPosition(), async contentItems => + if (!await _authorizationService.AuthorizeContentTypeDefinitionsAsync(context.User, CommonPermissions.ListContent, listableContentTypes, _contentManager)) { - if (!await _authorizationService.AuthorizeContentTypeDefinitionsAsync(context.User, CommonPermissions.ListContent, listableContentTypes, _contentManager)) - { - contentItems.Permission(CommonPermissions.ListContent); - } + contentItems.Permission(CommonPermissions.ListContent); + } - contentItems.Action(nameof(AdminController.List), typeof(AdminController).ControllerName(), _routeValues); - contentItems.LocalNav(); - }); + contentItems.Action(nameof(AdminController.List), typeof(AdminController).ControllerName(), _routeValues); + contentItems.LocalNav(); }); + }); - var adminSettings = await _siteService.GetSettingsAsync(); + var adminSettings = await _siteService.GetSettingsAsync(); - if (adminSettings.DisplayNewMenu) - { - var creatableContentTypes = contentTypeDefinitions.Where(ctd => ctd.IsCreatable()).OrderBy(ctd => ctd.DisplayName); + if (adminSettings.DisplayNewMenu) + { + var creatableContentTypes = contentTypeDefinitions.Where(ctd => ctd.IsCreatable()).OrderBy(ctd => ctd.DisplayName); - if (creatableContentTypes.Any()) + if (creatableContentTypes.Any()) + { + await builder.AddAsync(S["New"], "-1", async newMenu => { - await builder.AddAsync(S["New"], "-1", async newMenu => + newMenu.LinkToFirstChild(false).AddClass("new").Id("new"); + foreach (var contentTypeDefinition in creatableContentTypes) { - newMenu.LinkToFirstChild(false).AddClass("new").Id("new"); - foreach (var contentTypeDefinition in creatableContentTypes) - { - var ci = await _contentManager.NewAsync(contentTypeDefinition.Name); - var cim = await _contentManager.PopulateAspectAsync(ci); - var createRouteValues = cim.CreateRouteValues; - createRouteValues.Add("returnUrl", _linkGenerator.GetPathByRouteValues(context, string.Empty, _adminListRouteValues)); + var ci = await _contentManager.NewAsync(contentTypeDefinition.Name); + var cim = await _contentManager.PopulateAspectAsync(ci); + var createRouteValues = cim.CreateRouteValues; + createRouteValues.Add("returnUrl", _linkGenerator.GetPathByRouteValues(context, string.Empty, _adminListRouteValues)); - if (createRouteValues.Count > 0) - { - newMenu.Add(new LocalizedString(contentTypeDefinition.DisplayName, contentTypeDefinition.DisplayName), "5", item => item - .Action(cim.CreateRouteValues["Action"] as string, cim.CreateRouteValues["Controller"] as string, cim.CreateRouteValues) - .Permission(ContentTypePermissionsHelper.CreateDynamicPermission(ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditOwnContent.Name], contentTypeDefinition)) - ); - } + if (createRouteValues.Count > 0) + { + newMenu.Add(new LocalizedString(contentTypeDefinition.DisplayName, contentTypeDefinition.DisplayName), "5", item => item + .Action(cim.CreateRouteValues["Action"] as string, cim.CreateRouteValues["Controller"] as string, cim.CreateRouteValues) + .Permission(ContentTypePermissionsHelper.CreateDynamicPermission(ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditOwnContent.Name], contentTypeDefinition)) + ); } - }); - } + } + }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNode.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNode.cs index 75532047ac5..52f0a0d8f98 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNode.cs @@ -1,17 +1,16 @@ using OrchardCore.AdminMenu.Models; -namespace OrchardCore.Contents.AdminNodes +namespace OrchardCore.Contents.AdminNodes; + +public class ContentTypesAdminNode : AdminNode { - public class ContentTypesAdminNode : AdminNode - { - public bool ShowAll { get; set; } - public string IconClass { get; set; } - public ContentTypeEntry[] ContentTypes { get; set; } = []; - } + public bool ShowAll { get; set; } + public string IconClass { get; set; } + public ContentTypeEntry[] ContentTypes { get; set; } = []; +} - public class ContentTypeEntry - { - public string ContentTypeId { get; set; } - public string IconClass { get; set; } - } +public class ContentTypeEntry +{ + public string ContentTypeId { get; set; } + public string IconClass { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeDriver.cs index 98d3e322074..deb6973d75e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeDriver.cs @@ -10,91 +10,90 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Navigation; -namespace OrchardCore.Contents.AdminNodes +namespace OrchardCore.Contents.AdminNodes; + +public sealed class ContentTypesAdminNodeDriver : DisplayDriver { - public sealed class ContentTypesAdminNodeDriver : DisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentTypesAdminNodeDriver( + IContentDefinitionManager contentDefinitionManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - - public ContentTypesAdminNodeDriver( - IContentDefinitionManager contentDefinitionManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService - ) - { - _contentDefinitionManager = contentDefinitionManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + _contentDefinitionManager = contentDefinitionManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public override Task DisplayAsync(ContentTypesAdminNode treeNode, BuildDisplayContext context) - { - return CombineAsync( - View("ContentTypesAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), - View("ContentTypesAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") - ); - } + public override Task DisplayAsync(ContentTypesAdminNode treeNode, BuildDisplayContext context) + { + return CombineAsync( + View("ContentTypesAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), + View("ContentTypesAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") + ); + } - public override IDisplayResult Edit(ContentTypesAdminNode treeNode, BuildEditorContext context) - { - return Initialize("ContentTypesAdminNode_Fields_TreeEdit", async model => + public override IDisplayResult Edit(ContentTypesAdminNode treeNode, BuildEditorContext context) + { + return Initialize("ContentTypesAdminNode_Fields_TreeEdit", async model => + { + var listable = await GetListableContentTypeDefinitionsAsync(); + + model.ShowAll = treeNode.ShowAll; + model.IconClass = treeNode.IconClass; + model.ContentTypes = listable.Select(x => new ContentTypeEntryViewModel { - var listable = await GetListableContentTypeDefinitionsAsync(); - - model.ShowAll = treeNode.ShowAll; - model.IconClass = treeNode.IconClass; - model.ContentTypes = listable.Select(x => new ContentTypeEntryViewModel - { - ContentTypeId = x.Name, - IsChecked = treeNode.ContentTypes.Any(selected => string.Equals(selected.ContentTypeId, x.Name, StringComparison.OrdinalIgnoreCase)), - IconClass = treeNode.ContentTypes.FirstOrDefault(selected => selected.ContentTypeId == x.Name)?.IconClass ?? string.Empty - }).ToArray(); - }).Location("Content"); - } + ContentTypeId = x.Name, + IsChecked = treeNode.ContentTypes.Any(selected => string.Equals(selected.ContentTypeId, x.Name, StringComparison.OrdinalIgnoreCase)), + IconClass = treeNode.ContentTypes.FirstOrDefault(selected => selected.ContentTypeId == x.Name)?.IconClass ?? string.Empty + }).ToArray(); + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypesAdminNode treeNode, UpdateEditorContext context) - { - // Initializes the value to empty otherwise the model is not updated if no type is selected. - treeNode.ContentTypes = []; + public override async Task UpdateAsync(ContentTypesAdminNode treeNode, UpdateEditorContext context) + { + // Initializes the value to empty otherwise the model is not updated if no type is selected. + treeNode.ContentTypes = []; - var model = new ContentTypesAdminNodeViewModel(); + var model = new ContentTypesAdminNodeViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, x => x.ShowAll, x => x.IconClass, x => x.ContentTypes); + await context.Updater.TryUpdateModelAsync(model, Prefix, x => x.ShowAll, x => x.IconClass, x => x.ContentTypes); - treeNode.ShowAll = model.ShowAll; - treeNode.IconClass = model.IconClass; - treeNode.ContentTypes = model.ContentTypes - .Where(x => x.IsChecked == true) - .Select(x => - new ContentTypeEntry - { - ContentTypeId = x.ContentTypeId, - IconClass = x.IconClass - }) - .ToArray(); + treeNode.ShowAll = model.ShowAll; + treeNode.IconClass = model.IconClass; + treeNode.ContentTypes = model.ContentTypes + .Where(x => x.IsChecked == true) + .Select(x => + new ContentTypeEntry + { + ContentTypeId = x.ContentTypeId, + IconClass = x.IconClass + }) + .ToArray(); - return Edit(treeNode, context); - } + return Edit(treeNode, context); + } - private async Task> GetListableContentTypeDefinitionsAsync() - { - var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + private async Task> GetListableContentTypeDefinitionsAsync() + { + var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); - var listableContentTypeDefinitions = new List(); + var listableContentTypeDefinitions = new List(); - foreach (var contentTypeDefinition in contentTypeDefinitions) + foreach (var contentTypeDefinition in contentTypeDefinitions) + { + if (!await _authorizationService.AuthorizeContentTypeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ListContent, contentTypeDefinition)) { - if (!await _authorizationService.AuthorizeContentTypeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ListContent, contentTypeDefinition)) - { - continue; - } - - listableContentTypeDefinitions.Add(contentTypeDefinition); + continue; } - return listableContentTypeDefinitions.OrderBy(t => t.DisplayName); + listableContentTypeDefinitions.Add(contentTypeDefinition); } + + return listableContentTypeDefinitions.OrderBy(t => t.DisplayName); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeNavigationBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeNavigationBuilder.cs index 04055f04e13..3d5f8d6e0e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeNavigationBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeNavigationBuilder.cs @@ -13,125 +13,124 @@ using OrchardCore.Contents.Security; using OrchardCore.Navigation; -namespace OrchardCore.Contents.AdminNodes +namespace OrchardCore.Contents.AdminNodes; + +public class ContentTypesAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder { - public class ContentTypesAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder + private readonly LinkGenerator _linkGenerator; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ILogger _logger; + + public ContentTypesAdminNodeNavigationBuilder( + IContentDefinitionManager contentDefinitionManager, + LinkGenerator linkGenerator, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + ILogger logger) { - private readonly LinkGenerator _linkGenerator; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ILogger _logger; - - public ContentTypesAdminNodeNavigationBuilder( - IContentDefinitionManager contentDefinitionManager, - LinkGenerator linkGenerator, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - ILogger logger) - { - _contentDefinitionManager = contentDefinitionManager; - _linkGenerator = linkGenerator; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _logger = logger; - } + _contentDefinitionManager = contentDefinitionManager; + _linkGenerator = linkGenerator; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _logger = logger; + } + + public string Name => nameof(ContentTypesAdminNode); - public string Name => nameof(ContentTypesAdminNode); + public async Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + { + var node = menuItem as ContentTypesAdminNode; - public async Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + if (node == null || !node.Enabled) { - var node = menuItem as ContentTypesAdminNode; + return; + } - if (node == null || !node.Enabled) + // Add ContentTypes specific children + var typesToShow = await GetListableContentTypeDefinitionsAsync(node); + foreach (var ctd in typesToShow) + { + builder.Add(new LocalizedString(ctd.DisplayName, ctd.DisplayName), cTypeMenu => { - return; - } + cTypeMenu.Url(_linkGenerator.GetPathByRouteValues(_httpContextAccessor.HttpContext, string.Empty, new + { + area = "OrchardCore.Contents", + controller = "Admin", + action = "List", + contentTypeId = ctd.Name + })); + + cTypeMenu.Priority(node.Priority); + cTypeMenu.Position(node.Position); + cTypeMenu.Permission( + ContentTypePermissionsHelper.CreateDynamicPermission(ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.ViewContent.Name], ctd)); + + GetIconClasses(ctd, node).ToList().ForEach(c => cTypeMenu.AddClass(c)); + }); + } - // Add ContentTypes specific children - var typesToShow = await GetListableContentTypeDefinitionsAsync(node); - foreach (var ctd in typesToShow) + // Add external children + foreach (var childNode in node.Items) + { + try { - builder.Add(new LocalizedString(ctd.DisplayName, ctd.DisplayName), cTypeMenu => - { - cTypeMenu.Url(_linkGenerator.GetPathByRouteValues(_httpContextAccessor.HttpContext, string.Empty, new - { - area = "OrchardCore.Contents", - controller = "Admin", - action = "List", - contentTypeId = ctd.Name - })); - - cTypeMenu.Priority(node.Priority); - cTypeMenu.Position(node.Position); - cTypeMenu.Permission( - ContentTypePermissionsHelper.CreateDynamicPermission(ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.ViewContent.Name], ctd)); - - GetIconClasses(ctd, node).ToList().ForEach(c => cTypeMenu.AddClass(c)); - }); + var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childNode.GetType().Name); + await treeBuilder.BuildNavigationAsync(childNode, builder, treeNodeBuilders); } - - // Add external children - foreach (var childNode in node.Items) + catch (Exception e) { - try - { - var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childNode.GetType().Name); - await treeBuilder.BuildNavigationAsync(childNode, builder, treeNodeBuilders); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childNode.GetType().Name); - } + _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childNode.GetType().Name); } } + } - private async Task> GetListableContentTypeDefinitionsAsync(ContentTypesAdminNode node) - { - var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + private async Task> GetListableContentTypeDefinitionsAsync(ContentTypesAdminNode node) + { + var contentTypeDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); - var listableContentTypeDefinitions = new List(); + var listableContentTypeDefinitions = new List(); - foreach (var contentTypeDefinition in contentTypeDefinitions) + foreach (var contentTypeDefinition in contentTypeDefinitions) + { + if (!node.ShowAll && !node.ContentTypes.Any(entry => string.Equals(contentTypeDefinition.Name, entry.ContentTypeId, StringComparison.OrdinalIgnoreCase))) { - if (!node.ShowAll && !node.ContentTypes.Any(entry => string.Equals(contentTypeDefinition.Name, entry.ContentTypeId, StringComparison.OrdinalIgnoreCase))) - { - continue; - } - - if (!await _authorizationService.AuthorizeContentTypeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ListContent, contentTypeDefinition)) - { - continue; - } - - listableContentTypeDefinitions.Add(contentTypeDefinition); + continue; } - return listableContentTypeDefinitions.OrderBy(t => t.DisplayName); - } - - private static List GetIconClasses(ContentTypeDefinition contentType, ContentTypesAdminNode node) - { - if (node.ShowAll) + if (!await _authorizationService.AuthorizeContentTypeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ListContent, contentTypeDefinition)) { - return AddPrefixToClasses(node.IconClass); + continue; } - else - { - var typeEntry = node.ContentTypes - .FirstOrDefault(x => string.Equals(x.ContentTypeId, contentType.Name, StringComparison.OrdinalIgnoreCase)); - return AddPrefixToClasses(typeEntry.IconClass); - } + listableContentTypeDefinitions.Add(contentTypeDefinition); } - private static List AddPrefixToClasses(string unprefixed) + return listableContentTypeDefinitions.OrderBy(t => t.DisplayName); + } + + private static List GetIconClasses(ContentTypeDefinition contentType, ContentTypesAdminNode node) + { + if (node.ShowAll) { - return unprefixed?.Split(' ') - .ToList() - .Select(c => NavigationConstants.CssClassPrefix + c) - .ToList() - ?? []; + return AddPrefixToClasses(node.IconClass); } + else + { + var typeEntry = node.ContentTypes + .FirstOrDefault(x => string.Equals(x.ContentTypeId, contentType.Name, StringComparison.OrdinalIgnoreCase)); + + return AddPrefixToClasses(typeEntry.IconClass); + } + } + + private static List AddPrefixToClasses(string unprefixed) + { + return unprefixed?.Split(' ') + .ToList() + .Select(c => NavigationConstants.CssClassPrefix + c) + .ToList() + ?? []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeViewModel.cs index 84b832bc14b..6b0c1d1b770 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AdminNodes/ContentTypesAdminNodeViewModel.cs @@ -1,16 +1,15 @@ -namespace OrchardCore.Contents.AdminNodes +namespace OrchardCore.Contents.AdminNodes; + +public class ContentTypesAdminNodeViewModel { - public class ContentTypesAdminNodeViewModel - { - public bool ShowAll { get; set; } - public string IconClass { get; set; } - public ContentTypeEntryViewModel[] ContentTypes { get; set; } = []; - } + public bool ShowAll { get; set; } + public string IconClass { get; set; } + public ContentTypeEntryViewModel[] ContentTypes { get; set; } = []; +} - public class ContentTypeEntryViewModel - { - public bool IsChecked { get; set; } - public string ContentTypeId { get; set; } - public string IconClass { get; set; } - } +public class ContentTypeEntryViewModel +{ + public bool IsChecked { get; set; } + public string ContentTypeId { get; set; } + public string IconClass { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Controllers/AuditTrailContentController.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Controllers/AuditTrailContentController.cs index 50694720921..37d44aecde6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Controllers/AuditTrailContentController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Controllers/AuditTrailContentController.cs @@ -15,119 +15,118 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.Contents.AuditTrail.Controllers +namespace OrchardCore.Contents.AuditTrail.Controllers; + +[RequireFeatures("OrchardCore.AuditTrail")] +[Admin("AuditTrail/Content/{action}/{auditTrailEventId}", "{action}AuditTrailContent")] +public class AuditTrailContentController : Controller { - [RequireFeatures("OrchardCore.AuditTrail")] - [Admin("AuditTrail/Content/{action}/{auditTrailEventId}", "{action}AuditTrailContent")] - public class AuditTrailContentController : Controller + private readonly ISession _session; + private readonly IContentManager _contentManager; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + private readonly ILogger _logger; + + public AuditTrailContentController( + ISession session, + IContentManager contentManager, + IUpdateModelAccessor updateModelAccessor, + IAuthorizationService authorizationService, + IContentItemDisplayManager contentItemDisplayManager, + INotifier notifier, + IHtmlLocalizer htmlLocalizer, + ILogger logger) { - private readonly ISession _session; - private readonly IContentManager _contentManager; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - private readonly ILogger _logger; - - public AuditTrailContentController( - ISession session, - IContentManager contentManager, - IUpdateModelAccessor updateModelAccessor, - IAuthorizationService authorizationService, - IContentItemDisplayManager contentItemDisplayManager, - INotifier notifier, - IHtmlLocalizer htmlLocalizer, - ILogger logger) - { - _session = session; - _contentManager = contentManager; - _updateModelAccessor = updateModelAccessor; - _authorizationService = authorizationService; - _contentItemDisplayManager = contentItemDisplayManager; - _notifier = notifier; - H = htmlLocalizer; - _logger = logger; - } + _session = session; + _contentManager = contentManager; + _updateModelAccessor = updateModelAccessor; + _authorizationService = authorizationService; + _contentItemDisplayManager = contentItemDisplayManager; + _notifier = notifier; + H = htmlLocalizer; + _logger = logger; + } - public async Task Display(string auditTrailEventId) + public async Task Display(string auditTrailEventId) + { + var auditTrailContentEvent = (await _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => index.EventId == auditTrailEventId) + .FirstOrDefaultAsync()) + ?.As(); + + if (auditTrailContentEvent == null || auditTrailContentEvent.ContentItem == null) { - var auditTrailContentEvent = (await _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => index.EventId == auditTrailEventId) - .FirstOrDefaultAsync()) - ?.As(); + return NotFound(); + } - if (auditTrailContentEvent == null || auditTrailContentEvent.ContentItem == null) - { - return NotFound(); - } + var contentItem = auditTrailContentEvent.ContentItem; - var contentItem = auditTrailContentEvent.ContentItem; + contentItem.Id = 0; + contentItem.ContentItemVersionId = ""; + contentItem.Published = false; + contentItem.Latest = false; - contentItem.Id = 0; - contentItem.ContentItemVersionId = ""; - contentItem.Published = false; - contentItem.Latest = false; + contentItem = await _contentManager.LoadAsync(contentItem); - contentItem = await _contentManager.LoadAsync(contentItem); + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + var auditTrailPart = contentItem.As(); + if (auditTrailPart != null) + { + auditTrailPart.ShowComment = true; + } - var auditTrailPart = contentItem.As(); - if (auditTrailPart != null) - { - auditTrailPart.ShowComment = true; - } + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + model.Properties["VersionNumber"] = auditTrailContentEvent.VersionNumber; - model.Properties["VersionNumber"] = auditTrailContentEvent.VersionNumber; + return View(model); + } - return View(model); - } + [HttpPost] + public async Task Restore(string auditTrailEventId) + { + var contentItem = (await _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => index.EventId == auditTrailEventId) + .FirstOrDefaultAsync()) + ?.As() + ?.ContentItem; - [HttpPost] - public async Task Restore(string auditTrailEventId) + if (contentItem == null) { - var contentItem = (await _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => index.EventId == auditTrailEventId) - .FirstOrDefaultAsync()) - ?.As() - ?.ContentItem; + return NotFound(); + } - if (contentItem == null) - { - return NotFound(); - } + contentItem = await _contentManager.LoadAsync(contentItem); - contentItem = await _contentManager.LoadAsync(contentItem); + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, contentItem)) + { + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, contentItem)) - { - return Forbid(); - } + var result = await _contentManager.RestoreAsync(contentItem); - var result = await _contentManager.RestoreAsync(contentItem); + if (!result.Succeeded) + { + await _notifier.WarningAsync(H["'{0}' was not restored, the version is not valid.", contentItem.DisplayText]); - if (!result.Succeeded) + foreach (var error in result.Errors) { - await _notifier.WarningAsync(H["'{0}' was not restored, the version is not valid.", contentItem.DisplayText]); - - foreach (var error in result.Errors) - { - // Pass ErrorMessage as an argument to ensure it is encoded - await _notifier.WarningAsync(new LocalizedHtmlString(nameof(AuditTrailContentController.Restore), "{0}", false, error.ErrorMessage)); - } - - return RedirectToAction("Index", "Admin", new { area = "OrchardCore.AuditTrail" }); + // Pass ErrorMessage as an argument to ensure it is encoded + await _notifier.WarningAsync(new LocalizedHtmlString(nameof(AuditTrailContentController.Restore), "{0}", false, error.ErrorMessage)); } - await _notifier.SuccessAsync(H["'{0}' has been restored.", contentItem.DisplayText]); - return RedirectToAction("Index", "Admin", new { area = "OrchardCore.AuditTrail" }); } + + await _notifier.SuccessAsync(H["'{0}' has been restored.", contentItem.DisplayText]); + + return RedirectToAction("Index", "Admin", new { area = "OrchardCore.AuditTrail" }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentEventDisplayDriver.cs index bc9fff7470d..ae71fe60a9f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentEventDisplayDriver.cs @@ -14,111 +14,110 @@ using OrchardCore.Entities; using YesSql; -namespace OrchardCore.Contents.AuditTrail.Drivers +namespace OrchardCore.Contents.AuditTrail.Drivers; + +public sealed class AuditTrailContentEventDisplayDriver : AuditTrailEventSectionDisplayDriver { - public sealed class AuditTrailContentEventDisplayDriver : AuditTrailEventSectionDisplayDriver + private readonly Dictionary _latestVersionId = []; + private readonly IAuditTrailManager _auditTrailManager; + private readonly ISession _session; + + public AuditTrailContentEventDisplayDriver( + IAuditTrailManager auditTrailManager, + ISession session) { - private readonly Dictionary _latestVersionId = []; - private readonly IAuditTrailManager _auditTrailManager; - private readonly ISession _session; + _auditTrailManager = auditTrailManager; + _session = session; + } - public AuditTrailContentEventDisplayDriver( - IAuditTrailManager auditTrailManager, - ISession session) - { - _auditTrailManager = auditTrailManager; - _session = session; - } + public override async Task DisplayAsync(AuditTrailEvent auditTrailEvent, AuditTrailContentEvent contentEvent, BuildDisplayContext context) + { + var contentItemId = contentEvent.ContentItem.ContentItemId; - public override async Task DisplayAsync(AuditTrailEvent auditTrailEvent, AuditTrailContentEvent contentEvent, BuildDisplayContext context) + if (!_latestVersionId.TryGetValue(contentItemId, out var latestVersionId)) { - var contentItemId = contentEvent.ContentItem.ContentItemId; + latestVersionId = (await _session.QueryIndex(index => index.ContentItemId == contentItemId && index.Latest) + .FirstOrDefaultAsync()) + ?.ContentItemVersionId; - if (!_latestVersionId.TryGetValue(contentItemId, out var latestVersionId)) - { - latestVersionId = (await _session.QueryIndex(index => index.ContentItemId == contentItemId && index.Latest) - .FirstOrDefaultAsync()) - ?.ContentItemVersionId; + _latestVersionId[contentItemId] = latestVersionId; + } - _latestVersionId[contentItemId] = latestVersionId; - } + var descriptor = _auditTrailManager.DescribeEvent(auditTrailEvent); - var descriptor = _auditTrailManager.DescribeEvent(auditTrailEvent); + return Combine( + Initialize("AuditTrailContentEventData_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) + .Location("SummaryAdmin", "EventData:10"), + Initialize("AuditTrailContentEventContent_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) + .Location("SummaryAdmin", "Content:10"), + Initialize("AuditTrailContentEventActions_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) + .Location("SummaryAdmin", "Actions:5"), + Initialize("AuditTrailContentEventDetail_DetailAdmin", async m => + { + BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId); + var previousContentItem = (await _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => + index.Category == "Content" && + index.CreatedUtc <= auditTrailEvent.CreatedUtc && + index.EventId != auditTrailEvent.EventId && + index.CorrelationId == contentEvent.ContentItem.ContentItemId) + .OrderByDescending(index => index.Id) + .FirstOrDefaultAsync())? + .As() + .ContentItem; - return Combine( - Initialize("AuditTrailContentEventData_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) - .Location("SummaryAdmin", "EventData:10"), - Initialize("AuditTrailContentEventContent_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) - .Location("SummaryAdmin", "Content:10"), - Initialize("AuditTrailContentEventActions_SummaryAdmin", m => BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId)) - .Location("SummaryAdmin", "Actions:5"), - Initialize("AuditTrailContentEventDetail_DetailAdmin", async m => + if (previousContentItem != null) { - BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId); - var previousContentItem = (await _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => - index.Category == "Content" && - index.CreatedUtc <= auditTrailEvent.CreatedUtc && - index.EventId != auditTrailEvent.EventId && - index.CorrelationId == contentEvent.ContentItem.ContentItemId) - .OrderByDescending(index => index.Id) - .FirstOrDefaultAsync())? - .As() - .ContentItem; + var current = JObject.FromObject(contentEvent.ContentItem); + var previous = JObject.FromObject(previousContentItem); + previous.Remove(nameof(AuditTrailPart)); + current.Remove(nameof(AuditTrailPart)); - if (previousContentItem != null) - { - var current = JObject.FromObject(contentEvent.ContentItem); - var previous = JObject.FromObject(previousContentItem); - previous.Remove(nameof(AuditTrailPart)); - current.Remove(nameof(AuditTrailPart)); + m.PreviousContentItem = previousContentItem; - m.PreviousContentItem = previousContentItem; + m.Previous = previous.ToString(); + m.Current = current.ToString(); + } + }).Location("DetailAdmin", "Content:5"), + Initialize("AuditTrailContentEventDiff_DetailAdmin", async m => + { + BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId); + var previousContentItem = (await _session.Query(collection: AuditTrailEvent.Collection) + .Where(index => + index.Category == "Content" && + index.CreatedUtc <= auditTrailEvent.CreatedUtc && + index.EventId != auditTrailEvent.EventId && + index.CorrelationId == contentEvent.ContentItem.ContentItemId) + .OrderByDescending(index => index.Id) + .FirstOrDefaultAsync())? + .As() + .ContentItem; - m.Previous = previous.ToString(); - m.Current = current.ToString(); - } - }).Location("DetailAdmin", "Content:5"), - Initialize("AuditTrailContentEventDiff_DetailAdmin", async m => + if (previousContentItem != null) { - BuildSummaryViewModel(m, auditTrailEvent, contentEvent, descriptor, latestVersionId); - var previousContentItem = (await _session.Query(collection: AuditTrailEvent.Collection) - .Where(index => - index.Category == "Content" && - index.CreatedUtc <= auditTrailEvent.CreatedUtc && - index.EventId != auditTrailEvent.EventId && - index.CorrelationId == contentEvent.ContentItem.ContentItemId) - .OrderByDescending(index => index.Id) - .FirstOrDefaultAsync())? - .As() - .ContentItem; + var current = JObject.FromObject(contentEvent.ContentItem); + var previous = JObject.FromObject(previousContentItem); + previous.Remove(nameof(AuditTrailPart)); + current.Remove(nameof(AuditTrailPart)); - if (previousContentItem != null) - { - var current = JObject.FromObject(contentEvent.ContentItem); - var previous = JObject.FromObject(previousContentItem); - previous.Remove(nameof(AuditTrailPart)); - current.Remove(nameof(AuditTrailPart)); + m.PreviousContentItem = previousContentItem; - m.PreviousContentItem = previousContentItem; - - m.Previous = previous.ToString(); - m.Current = current.ToString(); - } - }).Location("DetailAdmin", "Content:5#Diff") - ); - } + m.Previous = previous.ToString(); + m.Current = current.ToString(); + } + }).Location("DetailAdmin", "Content:5#Diff") + ); + } - private static void BuildSummaryViewModel(AuditTrailContentEventViewModel m, AuditTrailEvent model, AuditTrailContentEvent contentEvent, AuditTrailEventDescriptor descriptor, string latestVersionId) - { - m.AuditTrailEvent = model; - m.Descriptor = descriptor; - m.Name = contentEvent.Name; - m.ContentItem = contentEvent.ContentItem; - m.VersionNumber = contentEvent.VersionNumber; - m.Comment = contentEvent.Comment; - m.LatestVersionId = latestVersionId; - m.ContentEvent = contentEvent; - } + private static void BuildSummaryViewModel(AuditTrailContentEventViewModel m, AuditTrailEvent model, AuditTrailContentEvent contentEvent, AuditTrailEventDescriptor descriptor, string latestVersionId) + { + m.AuditTrailEvent = model; + m.Descriptor = descriptor; + m.Name = contentEvent.Name; + m.ContentItem = contentEvent.ContentItem; + m.VersionNumber = contentEvent.VersionNumber; + m.Comment = contentEvent.Comment; + m.LatestVersionId = latestVersionId; + m.ContentEvent = contentEvent; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentsDriver.cs index a7f056de39e..cd01fdc4f2d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailContentsDriver.cs @@ -8,28 +8,27 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.AuditTrail.Drivers +namespace OrchardCore.Contents.AuditTrail.Drivers; + +public sealed class AuditTrailContentsDriver : ContentDisplayDriver { - public sealed class AuditTrailContentsDriver : ContentDisplayDriver - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; - public AuditTrailContentsDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + public AuditTrailContentsDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) + { + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public override Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) - { - return Task.FromResult( - Initialize("AuditTrailContentsAction_SummaryAdmin", m => m.ContentItem = contentItem) - .Location("SummaryAdmin", "ActionsMenu:10") - .RenderWhen(() => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ViewAuditTrail)) - ); - } + public override Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) + { + return Task.FromResult( + Initialize("AuditTrailContentsAction_SummaryAdmin", m => m.ContentItem = contentItem) + .Location("SummaryAdmin", "ActionsMenu:10") + .RenderWhen(() => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext?.User, AuditTrailPermissions.ViewAuditTrail)) + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartDisplayDriver.cs index e2230fc5c6a..00ec664ec65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartDisplayDriver.cs @@ -7,32 +7,31 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.AuditTrail.Drivers +namespace OrchardCore.Contents.AuditTrail.Drivers; + +public sealed class AuditTrailPartDisplayDriver : ContentPartDisplayDriver { - public sealed class AuditTrailPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Edit(AuditTrailPart part, BuildPartEditorContext context) { - public override IDisplayResult Edit(AuditTrailPart part, BuildPartEditorContext context) + var settings = context.TypePartDefinition.GetSettings(); + if (settings.ShowCommentInput) { - var settings = context.TypePartDefinition.GetSettings(); - if (settings.ShowCommentInput) + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetEditorShapeType(context), model => + if (part.ShowComment) { - if (part.ShowComment) - { - model.Comment = part.Comment; - } - }); - } - - return null; + model.Comment = part.Comment; + } + }); } - public override async Task UpdateAsync(AuditTrailPart part, UpdatePartEditorContext context) - { - await context.Updater.TryUpdateModelAsync(part, Prefix); + return null; + } - return Edit(part, context); - } + public override async Task UpdateAsync(AuditTrailPart part, UpdatePartEditorContext context) + { + await context.Updater.TryUpdateModelAsync(part, Prefix); + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartSettingsDisplayDriver.cs index 8c53c3d0afd..a7c415193b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/AuditTrailPartSettingsDisplayDriver.cs @@ -8,41 +8,40 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.AuditTrail.Drivers +namespace OrchardCore.Contents.AuditTrail.Drivers; + +public sealed class AuditTrailPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class AuditTrailPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition model, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition model, BuildEditorContext context) + if (!string.Equals(nameof(AuditTrailPart), model.PartDefinition.Name, StringComparison.Ordinal)) { - if (!string.Equals(nameof(AuditTrailPart), model.PartDefinition.Name, StringComparison.Ordinal)) - { - return null; - } - - return Initialize("AuditTrailPartSettings_Edit", viewModel => - { - var settings = model.GetSettings(); - viewModel.ShowCommentInput = settings.ShowCommentInput; - }).Location("Content"); + return null; } - public override async Task UpdateAsync(ContentTypePartDefinition model, UpdateTypePartEditorContext context) + return Initialize("AuditTrailPartSettings_Edit", viewModel => + { + var settings = model.GetSettings(); + viewModel.ShowCommentInput = settings.ShowCommentInput; + }).Location("Content"); + } + + public override async Task UpdateAsync(ContentTypePartDefinition model, UpdateTypePartEditorContext context) + { + if (!string.Equals(nameof(AuditTrailPart), model.PartDefinition.Name, StringComparison.Ordinal)) { - if (!string.Equals(nameof(AuditTrailPart), model.PartDefinition.Name, StringComparison.Ordinal)) - { - return null; - } + return null; + } - var viewModel = new AuditTrailPartSettingsViewModel(); + var viewModel = new AuditTrailPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, m => m.ShowCommentInput); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, m => m.ShowCommentInput); - context.Builder.WithSettings(new AuditTrailPartSettings - { - ShowCommentInput = viewModel.ShowCommentInput - }); + context.Builder.WithSettings(new AuditTrailPartSettings + { + ShowCommentInput = viewModel.ShowCommentInput + }); - return Edit(model, context); - } + return Edit(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/ContentAuditTrailSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/ContentAuditTrailSettingsDisplayDriver.cs index 58c6f94499e..ef031af8fa9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/ContentAuditTrailSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Drivers/ContentAuditTrailSettingsDisplayDriver.cs @@ -10,52 +10,51 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.Contents.AuditTrail.Drivers +namespace OrchardCore.Contents.AuditTrail.Drivers; + +public sealed class ContentAuditTrailSettingsDisplayDriver : SiteDisplayDriver { - public sealed class ContentAuditTrailSettingsDisplayDriver : SiteDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentAuditTrailSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public ContentAuditTrailSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + protected override string SettingsGroupId + => AuditTrailSettingsGroup.Id; + + public override async Task EditAsync(ISite site, ContentAuditTrailSettings section, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => AuditTrailSettingsGroup.Id; - - public override async Task EditAsync(ISite site, ContentAuditTrailSettings section, BuildEditorContext context) + return Initialize("ContentAuditTrailSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } - - return Initialize("ContentAuditTrailSettings_Edit", model => - { - model.AllowedContentTypes = section.AllowedContentTypes; - }).Location("Content:10#Content;5") - .OnGroup(SettingsGroupId); - } + model.AllowedContentTypes = section.AllowedContentTypes; + }).Location("Content:10#Content;5") + .OnGroup(SettingsGroupId); + } - public override async Task UpdateAsync(ISite site, ContentAuditTrailSettings section, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, ContentAuditTrailSettings section, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, AuditTrailPermissions.ManageAuditTrailSettings)) - { - return null; - } + return null; + } - var model = new ContentAuditTrailSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - section.AllowedContentTypes = model.AllowedContentTypes; + var model = new ContentAuditTrailSettings(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + section.AllowedContentTypes = model.AllowedContentTypes; - return await EditAsync(site, section, context); - } + return await EditAsync(site, section, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Extensions/ContentOrchardHelperExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Extensions/ContentOrchardHelperExtensions.cs index 2784effca04..ea9025ec767 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Extensions/ContentOrchardHelperExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Extensions/ContentOrchardHelperExtensions.cs @@ -7,38 +7,37 @@ using OrchardCore.ContentManagement; using OrchardCore.DisplayManagement; -namespace OrchardCore.Contents.AuditTrail.Extensions +namespace OrchardCore.Contents.AuditTrail.Extensions; + +public static class ContentOrchardHelperExtensions { - public static class ContentOrchardHelperExtensions + public static async Task EditForLinkAsync(this IOrchardHelper orchardHelper, string linkText, ContentItem contentItem) { - public static async Task EditForLinkAsync(this IOrchardHelper orchardHelper, string linkText, ContentItem contentItem) - { - var viewContextAccessor = orchardHelper.HttpContext.RequestServices.GetRequiredService(); - var viewContext = viewContextAccessor.ViewContext; - var helper = MakeHtmlHelper(viewContext, viewContext.ViewData); - var contentManager = orchardHelper.HttpContext.RequestServices.GetRequiredService(); - var metadata = await contentManager.PopulateAspectAsync(contentItem); - - if (string.IsNullOrEmpty(linkText)) - { - linkText = contentItem.ContentType; - } + var viewContextAccessor = orchardHelper.HttpContext.RequestServices.GetRequiredService(); + var viewContext = viewContextAccessor.ViewContext; + var helper = MakeHtmlHelper(viewContext, viewContext.ViewData); + var contentManager = orchardHelper.HttpContext.RequestServices.GetRequiredService(); + var metadata = await contentManager.PopulateAspectAsync(contentItem); - return helper.ActionLink(linkText, metadata.EditorRouteValues["action"].ToString(), metadata.EditorRouteValues); + if (string.IsNullOrEmpty(linkText)) + { + linkText = contentItem.ContentType; } - private static IHtmlHelper MakeHtmlHelper(ViewContext viewContext, ViewDataDictionary viewData) - { - var newHelper = viewContext.HttpContext.RequestServices.GetRequiredService(); + return helper.ActionLink(linkText, metadata.EditorRouteValues["action"].ToString(), metadata.EditorRouteValues); + } - var contextable = newHelper as IViewContextAware; - if (contextable != null) - { - var newViewContext = new ViewContext(viewContext, viewContext.View, viewData, viewContext.Writer); - contextable.Contextualize(newViewContext); - } + private static IHtmlHelper MakeHtmlHelper(ViewContext viewContext, ViewDataDictionary viewData) + { + var newHelper = viewContext.HttpContext.RequestServices.GetRequiredService(); - return newHelper; + var contextable = newHelper as IViewContextAware; + if (contextable != null) + { + var newViewContext = new ViewContext(viewContext, viewContext.View, viewData, viewContext.Writer); + contextable.Contextualize(newViewContext); } + + return newHelper; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Handlers/AuditTrailContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Handlers/AuditTrailContentHandler.cs index 9ec5cfa7ff2..b785c51bbc5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Handlers/AuditTrailContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Handlers/AuditTrailContentHandler.cs @@ -14,89 +14,88 @@ using OrchardCore.Settings; using YesSql; -namespace OrchardCore.Contents.AuditTrail.Handlers +namespace OrchardCore.Contents.AuditTrail.Handlers; + +public class AuditTrailContentHandler : ContentHandlerBase { - public class AuditTrailContentHandler : ContentHandlerBase + private readonly YesSql.ISession _session; + private readonly ISiteService _siteService; + private readonly IAuditTrailManager _auditTrailManager; + private readonly IHttpContextAccessor _httpContextAccessor; + + private readonly HashSet _restoring = []; + + public AuditTrailContentHandler( + YesSql.ISession session, + ISiteService siteService, + IAuditTrailManager auditTrailManager, + IHttpContextAccessor httpContextAccessor) { - private readonly YesSql.ISession _session; - private readonly ISiteService _siteService; - private readonly IAuditTrailManager _auditTrailManager; - private readonly IHttpContextAccessor _httpContextAccessor; - - private readonly HashSet _restoring = []; - - public AuditTrailContentHandler( - YesSql.ISession session, - ISiteService siteService, - IAuditTrailManager auditTrailManager, - IHttpContextAccessor httpContextAccessor) - { - _session = session; - _siteService = siteService; - _auditTrailManager = auditTrailManager; - _httpContextAccessor = httpContextAccessor; - } + _session = session; + _siteService = siteService; + _auditTrailManager = auditTrailManager; + _httpContextAccessor = httpContextAccessor; + } - public override Task DraftSavedAsync(SaveDraftContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Saved, context.ContentItem); + public override Task DraftSavedAsync(SaveDraftContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Saved, context.ContentItem); - public override Task CreatedAsync(CreateContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Created, context.ContentItem); + public override Task CreatedAsync(CreateContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Created, context.ContentItem); - public override Task PublishedAsync(PublishContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Published, context.ContentItem); + public override Task PublishedAsync(PublishContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Published, context.ContentItem); - public override Task UnpublishedAsync(PublishContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Unpublished, context.ContentItem); + public override Task UnpublishedAsync(PublishContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Unpublished, context.ContentItem); - public override Task RemovedAsync(RemoveContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Removed, context.ContentItem); + public override Task RemovedAsync(RemoveContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Removed, context.ContentItem); - public override Task ClonedAsync(CloneContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Cloned, context.ContentItem); + public override Task ClonedAsync(CloneContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Cloned, context.ContentItem); - public override Task RestoringAsync(RestoreContentContext context) - { - _restoring.Add(context.ContentItem.ContentItemId); + public override Task RestoringAsync(RestoreContentContext context) + { + _restoring.Add(context.ContentItem.ContentItemId); - return Task.CompletedTask; + return Task.CompletedTask; + } + + public override Task RestoredAsync(RestoreContentContext context) + => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Restored, context.ContentItem); + + private async Task RecordAuditTrailEventAsync(string name, ContentItem content) + { + if (name != ContentAuditTrailEventConfiguration.Restored && _restoring.Contains(content.ContentItem.ContentItemId)) + { + return; } - public override Task RestoredAsync(RestoreContentContext context) - => RecordAuditTrailEventAsync(ContentAuditTrailEventConfiguration.Restored, context.ContentItem); + var settings = await _siteService.GetSettingsAsync(); - private async Task RecordAuditTrailEventAsync(string name, ContentItem content) + if (!settings.AllowedContentTypes.Contains(content.ContentItem.ContentType)) { - if (name != ContentAuditTrailEventConfiguration.Restored && _restoring.Contains(content.ContentItem.ContentItemId)) - { - return; - } - - var settings = await _siteService.GetSettingsAsync(); - - if (!settings.AllowedContentTypes.Contains(content.ContentItem.ContentType)) - { - return; - } - - var versionNumber = await _session - .QueryIndex(index => index.ContentItemId == content.ContentItem.ContentItemId) - .CountAsync(); - - await _auditTrailManager.RecordEventAsync( - new AuditTrailContext - ( - name, - ContentAuditTrailEventConfiguration.Content, - content.ContentItem.ContentItemId, - _httpContextAccessor.HttpContext.User?.FindFirstValue(ClaimTypes.NameIdentifier), - _httpContextAccessor.HttpContext.User?.Identity?.Name, - new AuditTrailContentEvent - { - ContentItem = content.ContentItem, - VersionNumber = versionNumber - } - )); + return; } + + var versionNumber = await _session + .QueryIndex(index => index.ContentItemId == content.ContentItem.ContentItemId) + .CountAsync(); + + await _auditTrailManager.RecordEventAsync( + new AuditTrailContext + ( + name, + ContentAuditTrailEventConfiguration.Content, + content.ContentItem.ContentItemId, + _httpContextAccessor.HttpContext.User?.FindFirstValue(ClaimTypes.NameIdentifier), + _httpContextAccessor.HttpContext.User?.Identity?.Name, + new AuditTrailContentEvent + { + ContentItem = content.ContentItem, + VersionNumber = versionNumber + } + )); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Migrations.cs index ec251ceb009..30446da764a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Migrations.cs @@ -4,25 +4,24 @@ using OrchardCore.Data.Migration; using OrchardCore.Modules; -namespace OrchardCore.Contents.AuditTrail +namespace OrchardCore.Contents.AuditTrail; + +[RequireFeatures("OrchardCore.AuditTrail")] +public sealed class Migrations : DataMigration { - [RequireFeatures("OrchardCore.AuditTrail")] - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("AuditTrailPart", part => part - .Attachable() - .WithDescription("Allows editors to enter a comment to be saved into the Audit Trail event when saving a content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("AuditTrailPart", part => part + .Attachable() + .WithDescription("Allows editors to enter a comment to be saved into the Audit Trail event when saving a content item.")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailContentEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailContentEvent.cs index ecb09e953c3..f1f9cfd62d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailContentEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailContentEvent.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.AuditTrail.Models +namespace OrchardCore.Contents.AuditTrail.Models; + +public class AuditTrailContentEvent { - public class AuditTrailContentEvent - { - public string Name { get; set; } = "Content"; - public ContentItem ContentItem { get; set; } - public int VersionNumber { get; set; } - public string Comment { get; set; } - } + public string Name { get; set; } = "Content"; + public ContentItem ContentItem { get; set; } + public int VersionNumber { get; set; } + public string Comment { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailPart.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailPart.cs index 84017ffec0d..b3afa2f33db 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Models/AuditTrailPart.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.AuditTrail.Models +namespace OrchardCore.Contents.AuditTrail.Models; + +public class AuditTrailPart : ContentPart { - public class AuditTrailPart : ContentPart - { - public string Comment { get; set; } - public bool ShowComment { get; set; } - } + public string Comment { get; set; } + public bool ShowComment { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventConfiguration.cs index 9328b5f0b25..1a8070dc72c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventConfiguration.cs @@ -1,29 +1,28 @@ using Microsoft.Extensions.Options; using OrchardCore.AuditTrail.Services.Models; -namespace OrchardCore.Contents.AuditTrail.Services +namespace OrchardCore.Contents.AuditTrail.Services; + +public sealed class ContentAuditTrailEventConfiguration : IConfigureOptions { - public sealed class ContentAuditTrailEventConfiguration : IConfigureOptions - { - public const string Content = nameof(Content); - public const string Created = nameof(Created); - public const string Saved = nameof(Saved); - public const string Published = nameof(Published); - public const string Unpublished = nameof(Unpublished); - public const string Removed = nameof(Removed); - public const string Cloned = nameof(Cloned); - public const string Restored = nameof(Restored); + public const string Content = nameof(Content); + public const string Created = nameof(Created); + public const string Saved = nameof(Saved); + public const string Published = nameof(Published); + public const string Unpublished = nameof(Unpublished); + public const string Removed = nameof(Removed); + public const string Cloned = nameof(Cloned); + public const string Restored = nameof(Restored); - public void Configure(AuditTrailOptions options) - { - options.For(Content, S => S["Content"]) - .WithEvent(Created, S => S["Created"], S => S["A content item was created."], true) - .WithEvent(Saved, S => S["Saved"], S => S["A content item was saved."], true) - .WithEvent(Published, S => S["Published"], S => S["A content item was published."], true) - .WithEvent(Unpublished, S => S["Unpublished"], S => S["A content item was unpublished."], true) - .WithEvent(Removed, S => S["Removed"], S => S["A content item was deleted."], true) - .WithEvent(Cloned, S => S["Cloned"], S => S["A content item was cloned."], true) - .WithEvent(Restored, S => S["Restored"], S => S["A content item was restored to a previous version."], true); - } + public void Configure(AuditTrailOptions options) + { + options.For(Content, S => S["Content"]) + .WithEvent(Created, S => S["Created"], S => S["A content item was created."], true) + .WithEvent(Saved, S => S["Saved"], S => S["A content item was saved."], true) + .WithEvent(Published, S => S["Published"], S => S["A content item was published."], true) + .WithEvent(Unpublished, S => S["Unpublished"], S => S["A content item was unpublished."], true) + .WithEvent(Removed, S => S["Removed"], S => S["A content item was deleted."], true) + .WithEvent(Cloned, S => S["Cloned"], S => S["A content item was cloned."], true) + .WithEvent(Restored, S => S["Restored"], S => S["A content item was restored to a previous version."], true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventHandler.cs index 57a2a8f978e..3a7926f8d0a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Services/ContentAuditTrailEventHandler.cs @@ -5,29 +5,28 @@ using OrchardCore.Contents.AuditTrail.Models; using OrchardCore.Entities; -namespace OrchardCore.Contents.AuditTrail.Services +namespace OrchardCore.Contents.AuditTrail.Services; + +public class ContentAuditTrailEventHandler : AuditTrailEventHandlerBase { - public class ContentAuditTrailEventHandler : AuditTrailEventHandlerBase + public override Task CreateAsync(AuditTrailCreateContext context) { - public override Task CreateAsync(AuditTrailCreateContext context) + if (context.Category != "Content") { - if (context.Category != "Content") - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - if (context is AuditTrailCreateContext contentEvent) + if (context is AuditTrailCreateContext contentEvent) + { + var auditTrailPart = contentEvent.AuditTrailEventItem.ContentItem.As(); + if (auditTrailPart == null) { - var auditTrailPart = contentEvent.AuditTrailEventItem.ContentItem.As(); - if (auditTrailPart == null) - { - return Task.CompletedTask; - } - - contentEvent.AuditTrailEventItem.Comment = auditTrailPart.Comment; + return Task.CompletedTask; } - return Task.CompletedTask; + contentEvent.AuditTrailEventItem.Comment = auditTrailPart.Comment; } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/AuditTrailPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/AuditTrailPartSettings.cs index 2ed4e3ba450..ec125200c68 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/AuditTrailPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/AuditTrailPartSettings.cs @@ -1,10 +1,9 @@ using System.ComponentModel; -namespace OrchardCore.Contents.AuditTrail.Settings +namespace OrchardCore.Contents.AuditTrail.Settings; + +public class AuditTrailPartSettings { - public class AuditTrailPartSettings - { - [DefaultValue(true)] - public bool ShowCommentInput { get; set; } = true; - } + [DefaultValue(true)] + public bool ShowCommentInput { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/ContentAuditTrailSettings.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/ContentAuditTrailSettings.cs index a7073724c9e..6ea3e7e195f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/ContentAuditTrailSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Settings/ContentAuditTrailSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.AuditTrail.Settings +namespace OrchardCore.Contents.AuditTrail.Settings; + +public class ContentAuditTrailSettings { - public class ContentAuditTrailSettings - { - public string[] AllowedContentTypes { get; set; } = []; - } + public string[] AllowedContentTypes { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Startup.cs index d0af5218847..8e9e4d50b5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/Startup.cs @@ -16,27 +16,26 @@ using OrchardCore.Modules; using OrchardCore.Settings; -namespace OrchardCore.Contents.AuditTrail +namespace OrchardCore.Contents.AuditTrail; + +[RequireFeatures("OrchardCore.AuditTrail")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.AuditTrail")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddDataMigration(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddTransient, ContentAuditTrailEventConfiguration>(); - services.AddScoped(); - services.AddScoped, ContentAuditTrailSettingsDisplayDriver>(); + services.AddTransient, ContentAuditTrailEventConfiguration>(); + services.AddScoped(); + services.AddScoped, ContentAuditTrailSettingsDisplayDriver>(); - services.AddScoped, AuditTrailContentEventDisplayDriver>(); + services.AddScoped, AuditTrailContentEventDisplayDriver>(); - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventDetailViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventDetailViewModel.cs index e70f262f228..1a54a646383 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventDetailViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventDetailViewModel.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.AuditTrail.ViewModels +namespace OrchardCore.Contents.AuditTrail.ViewModels; + +public class AuditTrailContentEventDetailViewModel : AuditTrailContentEventViewModel { - public class AuditTrailContentEventDetailViewModel : AuditTrailContentEventViewModel - { - public string Previous { get; set; } - public string Current { get; set; } - public ContentItem PreviousContentItem { get; set; } - } + public string Previous { get; set; } + public string Current { get; set; } + public ContentItem PreviousContentItem { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventViewModel.cs index a1314f71bbe..04c7a4e3358 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailContentEventViewModel.cs @@ -4,23 +4,22 @@ using OrchardCore.ContentManagement; using OrchardCore.Contents.AuditTrail.Models; -namespace OrchardCore.Contents.AuditTrail.ViewModels +namespace OrchardCore.Contents.AuditTrail.ViewModels; + +public class AuditTrailContentEventViewModel { - public class AuditTrailContentEventViewModel - { - public string Name { get; set; } - public ContentItem ContentItem { get; set; } - public int VersionNumber { get; set; } - public string LatestVersionId { get; set; } - public string Comment { get; set; } + public string Name { get; set; } + public ContentItem ContentItem { get; set; } + public int VersionNumber { get; set; } + public string LatestVersionId { get; set; } + public string Comment { get; set; } - [BindNever] - public AuditTrailEventDescriptor Descriptor { get; set; } + [BindNever] + public AuditTrailEventDescriptor Descriptor { get; set; } - [BindNever] - public AuditTrailContentEvent ContentEvent { get; set; } + [BindNever] + public AuditTrailContentEvent ContentEvent { get; set; } - [BindNever] - public AuditTrailEvent AuditTrailEvent { get; set; } - } + [BindNever] + public AuditTrailEvent AuditTrailEvent { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartSettingsViewModel.cs index 896c6e311d1..37c24e169c8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.AuditTrail.ViewModels +namespace OrchardCore.Contents.AuditTrail.ViewModels; + +public class AuditTrailPartSettingsViewModel { - public class AuditTrailPartSettingsViewModel - { - public bool ShowCommentInput { get; set; } - } + public bool ShowCommentInput { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartViewModel.cs index f16f389893e..1dcd2380c48 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/AuditTrailPartViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.AuditTrail.ViewModels +namespace OrchardCore.Contents.AuditTrail.ViewModels; + +public class AuditTrailPartViewModel { - public class AuditTrailPartViewModel - { - public string Comment { get; set; } - } + public string Comment { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/ContentAuditTrailSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/ContentAuditTrailSettingsViewModel.cs index 87b90d5ab4d..ba1e3dadc39 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/ContentAuditTrailSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/AuditTrail/ViewModels/ContentAuditTrailSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.AuditTrail.ViewModels +namespace OrchardCore.Contents.AuditTrail.ViewModels; + +public class ContentAuditTrailSettingsViewModel { - public class ContentAuditTrailSettingsViewModel - { - public string[] AllowedContentTypes { get; set; } = []; - } + public string[] AllowedContentTypes { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/AdminController.cs index dacc7472b18..28e997d21b9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/AdminController.cs @@ -28,778 +28,777 @@ using YesSql.Filters.Query; using YesSql.Services; -namespace OrchardCore.Contents.Controllers +namespace OrchardCore.Contents.Controllers; + +public class AdminController : Controller, IUpdateModel { - public class AdminController : Controller, IUpdateModel + private readonly IAuthorizationService _authorizationService; + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IDisplayManager _contentOptionsDisplayManager; + private readonly ISession _session; + private readonly INotifier _notifier; + + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + + public AdminController( + IAuthorizationService authorizationService, + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + IContentDefinitionManager contentDefinitionManager, + IDisplayManager contentOptionsDisplayManager, + ISession session, + INotifier notifier, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer) { - private readonly IAuthorizationService _authorizationService; - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IDisplayManager _contentOptionsDisplayManager; - private readonly ISession _session; - private readonly INotifier _notifier; - - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - - public AdminController( - IAuthorizationService authorizationService, - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - IContentDefinitionManager contentDefinitionManager, - IDisplayManager contentOptionsDisplayManager, - ISession session, - INotifier notifier, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer) - { - _authorizationService = authorizationService; - _contentManager = contentManager; - _contentItemDisplayManager = contentItemDisplayManager; - _contentDefinitionManager = contentDefinitionManager; - _contentOptionsDisplayManager = contentOptionsDisplayManager; - _session = session; - _notifier = notifier; - - H = htmlLocalizer; - S = stringLocalizer; - } - - [Admin("Contents/ContentItems/{contentTypeId?}", "ListContentItems")] - public async Task List( - [FromServices] IOptions pagerOptions, - [FromServices] IShapeFactory shapeFactory, - [FromServices] IContentsAdminListQueryService contentsAdminListQueryService, - [ModelBinder(BinderType = typeof(ContentItemFilterEngineModelBinder), Name = "q")] QueryFilterResult queryFilterResult, - ContentOptionsViewModel options, - PagerParameters pagerParameters, - string contentTypeId = "", - string stereotype = "") - { - var contentTypeDefinitions = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .OrderBy(ctd => ctd.DisplayName) - .ToArray(); - - if (!await _authorizationService.AuthorizeContentTypeDefinitionsAsync(User, CommonPermissions.ListContent, contentTypeDefinitions, _contentManager)) - { - return Forbid(); - } + _authorizationService = authorizationService; + _contentManager = contentManager; + _contentItemDisplayManager = contentItemDisplayManager; + _contentDefinitionManager = contentDefinitionManager; + _contentOptionsDisplayManager = contentOptionsDisplayManager; + _session = session; + _notifier = notifier; + + H = htmlLocalizer; + S = stringLocalizer; + } - // The parameter contentTypeId is used by the AdminMenus. Pass it to the options. - if (!string.IsNullOrEmpty(contentTypeId)) - { - options.SelectedContentType = contentTypeId; - } + [Admin("Contents/ContentItems/{contentTypeId?}", "ListContentItems")] + public async Task List( + [FromServices] IOptions pagerOptions, + [FromServices] IShapeFactory shapeFactory, + [FromServices] IContentsAdminListQueryService contentsAdminListQueryService, + [ModelBinder(BinderType = typeof(ContentItemFilterEngineModelBinder), Name = "q")] QueryFilterResult queryFilterResult, + ContentOptionsViewModel options, + PagerParameters pagerParameters, + string contentTypeId = "", + string stereotype = "") + { + var contentTypeDefinitions = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .OrderBy(ctd => ctd.DisplayName) + .ToArray(); - // The filter is bound separately and mapped to the options. - // The options must still be bound so that options that are not filters are still bound. - options.FilterResult = queryFilterResult; + if (!await _authorizationService.AuthorizeContentTypeDefinitionsAsync(User, CommonPermissions.ListContent, contentTypeDefinitions, _contentManager)) + { + return Forbid(); + } - var hasSelectedContentType = !string.IsNullOrEmpty(options.SelectedContentType); + // The parameter contentTypeId is used by the AdminMenus. Pass it to the options. + if (!string.IsNullOrEmpty(contentTypeId)) + { + options.SelectedContentType = contentTypeId; + } - if (hasSelectedContentType) - { - // When the selected content type is provided via the route or options a placeholder node is used to apply a filter. - options.FilterResult.TryAddOrReplace(new ContentTypeFilterNode(options.SelectedContentType)); + // The filter is bound separately and mapped to the options. + // The options must still be bound so that options that are not filters are still bound. + options.FilterResult = queryFilterResult; - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(options.SelectedContentType); - if (contentTypeDefinition == null) - { - return NotFound(); - } + var hasSelectedContentType = !string.IsNullOrEmpty(options.SelectedContentType); - options.CreatableTypes = await GetCreatableTypeOptionsAsync(options.CanCreateSelectedContentType, contentTypeDefinition); - } + if (hasSelectedContentType) + { + // When the selected content type is provided via the route or options a placeholder node is used to apply a filter. + options.FilterResult.TryAddOrReplace(new ContentTypeFilterNode(options.SelectedContentType)); - if (!hasSelectedContentType && !string.IsNullOrEmpty(stereotype)) + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(options.SelectedContentType); + if (contentTypeDefinition == null) { - // When a stereotype is provided via the query parameter or options a placeholder node is used to apply a filter. - options.FilterResult.TryAddOrReplace(new StereotypeFilterNode(stereotype)); - - var availableContentTypeDefinitions = contentTypeDefinitions - .Where(definition => definition.StereotypeEquals(stereotype, StringComparison.OrdinalIgnoreCase)) - .ToArray(); - - if (availableContentTypeDefinitions.Length > 0) - { - options.ContentTypeOptions = await GetListableContentTypeOptionsAsync(availableContentTypeDefinitions, options.SelectedContentType, false); - options.CreatableTypes = await GetCreatableTypeOptionsAsync(options.CanCreateSelectedContentType, availableContentTypeDefinitions); - } + return NotFound(); } - if (options.CreatableTypes == null) - { - // At this point, the creatable types were not yet populated. Populate them using all creatable types. - var creatableContentTypeDefinitions = contentTypeDefinitions - .Where(ctd => ctd.IsCreatable()) - .ToArray(); + options.CreatableTypes = await GetCreatableTypeOptionsAsync(options.CanCreateSelectedContentType, contentTypeDefinition); + } - options.CreatableTypes = await GetCreatableTypeOptionsAsync(false, creatableContentTypeDefinitions); - } + if (!hasSelectedContentType && !string.IsNullOrEmpty(stereotype)) + { + // When a stereotype is provided via the query parameter or options a placeholder node is used to apply a filter. + options.FilterResult.TryAddOrReplace(new StereotypeFilterNode(stereotype)); - // We populate the remaining SelectLists. - options.ContentStatuses = - [ - new SelectListItem(S["Latest"], nameof(ContentsStatus.Latest)), - new SelectListItem(S["Published"], nameof(ContentsStatus.Published)), - new SelectListItem(S["Unpublished"], nameof(ContentsStatus.Draft)), - new SelectListItem(S["All versions"], nameof(ContentsStatus.AllVersions)), - ]; + var availableContentTypeDefinitions = contentTypeDefinitions + .Where(definition => definition.StereotypeEquals(stereotype, StringComparison.OrdinalIgnoreCase)) + .ToArray(); - if (await IsAuthorizedAsync(CommonPermissions.ListContent)) + if (availableContentTypeDefinitions.Length > 0) { - options.ContentStatuses.Insert(1, new SelectListItem(S["Owned by me"], nameof(ContentsStatus.Owner))); + options.ContentTypeOptions = await GetListableContentTypeOptionsAsync(availableContentTypeDefinitions, options.SelectedContentType, false); + options.CreatableTypes = await GetCreatableTypeOptionsAsync(options.CanCreateSelectedContentType, availableContentTypeDefinitions); } + } - options.ContentSorts = - [ - new SelectListItem(S["Recently created"], nameof(ContentsOrder.Created)), - new SelectListItem(S["Recently modified"], nameof(ContentsOrder.Modified)), - new SelectListItem(S["Recently published"], nameof(ContentsOrder.Published)), - new SelectListItem(S["Title"], nameof(ContentsOrder.Title)), - ]; - - options.ContentsBulkAction = - [ - new SelectListItem(S["Publish Now"], nameof(ContentsBulkAction.PublishNow)), - new SelectListItem(S["Unpublish"], nameof(ContentsBulkAction.Unpublish)), - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; - - if (options.ContentTypeOptions == null - && (string.IsNullOrEmpty(options.SelectedContentType) || string.IsNullOrEmpty(contentTypeId))) - { - options.ContentTypeOptions = await GetListableContentTypeOptionsAsync(contentTypeDefinitions, options.SelectedContentType, true); - } + if (options.CreatableTypes == null) + { + // At this point, the creatable types were not yet populated. Populate them using all creatable types. + var creatableContentTypeDefinitions = contentTypeDefinitions + .Where(ctd => ctd.IsCreatable()) + .ToArray(); - // If ContentTypeOptions is not initialized by query string or by the code above, initialize it. - options.ContentTypeOptions ??= []; + options.CreatableTypes = await GetCreatableTypeOptionsAsync(false, creatableContentTypeDefinitions); + } - // With the populated options, filter the query allowing the filters to alter the options. - var query = await contentsAdminListQueryService.QueryAsync(options, this); + // We populate the remaining SelectLists. + options.ContentStatuses = + [ + new SelectListItem(S["Latest"], nameof(ContentsStatus.Latest)), + new SelectListItem(S["Published"], nameof(ContentsStatus.Published)), + new SelectListItem(S["Unpublished"], nameof(ContentsStatus.Draft)), + new SelectListItem(S["All versions"], nameof(ContentsStatus.AllVersions)), + ]; - // The search text is provided back to the UI. - options.SearchText = options.FilterResult.ToString(); - options.OriginalSearchText = options.SearchText; + if (await IsAuthorizedAsync(CommonPermissions.ListContent)) + { + options.ContentStatuses.Insert(1, new SelectListItem(S["Owned by me"], nameof(ContentsStatus.Owner))); + } - // Populate route values to maintain previous route data when generating page links. - options.RouteValues.TryAdd("q", options.FilterResult.ToString()); + options.ContentSorts = + [ + new SelectListItem(S["Recently created"], nameof(ContentsOrder.Created)), + new SelectListItem(S["Recently modified"], nameof(ContentsOrder.Modified)), + new SelectListItem(S["Recently published"], nameof(ContentsOrder.Published)), + new SelectListItem(S["Title"], nameof(ContentsOrder.Title)), + ]; + + options.ContentsBulkAction = + [ + new SelectListItem(S["Publish Now"], nameof(ContentsBulkAction.PublishNow)), + new SelectListItem(S["Unpublish"], nameof(ContentsBulkAction.Unpublish)), + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; + + if (options.ContentTypeOptions == null + && (string.IsNullOrEmpty(options.SelectedContentType) || string.IsNullOrEmpty(contentTypeId))) + { + options.ContentTypeOptions = await GetListableContentTypeOptionsAsync(contentTypeDefinitions, options.SelectedContentType, true); + } - var pager = new Pager(pagerParameters, pagerOptions.Value.GetPageSize()); + // If ContentTypeOptions is not initialized by query string or by the code above, initialize it. + options.ContentTypeOptions ??= []; - var itemsPerPage = pagerOptions.Value.MaxPagedCount > 0 - ? pagerOptions.Value.MaxPagedCount - : await query.CountAsync(); + // With the populated options, filter the query allowing the filters to alter the options. + var query = await contentsAdminListQueryService.QueryAsync(options, this); - var pagerShape = await shapeFactory.PagerAsync(pager, itemsPerPage, options.RouteValues); + // The search text is provided back to the UI. + options.SearchText = options.FilterResult.ToString(); + options.OriginalSearchText = options.SearchText; - // Load items so that loading handlers are invoked. - var pageOfContentItems = await query.Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ListAsync(_contentManager); + // Populate route values to maintain previous route data when generating page links. + options.RouteValues.TryAdd("q", options.FilterResult.ToString()); - // We prepare the content items SummaryAdmin shape. - var contentItemSummaries = new List(); - foreach (var contentItem in pageOfContentItems) - { - contentItemSummaries.Add(await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this, "SummaryAdmin")); - } + var pager = new Pager(pagerParameters, pagerOptions.Value.GetPageSize()); - // Populate options pager summary values. - var startIndex = (pager.Page - 1) * pager.PageSize + 1; - options.StartIndex = startIndex; - options.EndIndex = startIndex + contentItemSummaries.Count - 1; - options.ContentItemsCount = contentItemSummaries.Count; - options.TotalItemCount = itemsPerPage; + var itemsPerPage = pagerOptions.Value.MaxPagedCount > 0 + ? pagerOptions.Value.MaxPagedCount + : await query.CountAsync(); - var header = await _contentOptionsDisplayManager.BuildEditorAsync(options, this, false, string.Empty, string.Empty); + var pagerShape = await shapeFactory.PagerAsync(pager, itemsPerPage, options.RouteValues); - var shapeViewModel = await shapeFactory.CreateAsync("ContentsAdminList", viewModel => - { - viewModel.ContentItems = contentItemSummaries; - viewModel.Pager = pagerShape; - viewModel.Options = options; - viewModel.Header = header; - }); + // Load items so that loading handlers are invoked. + var pageOfContentItems = await query.Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ListAsync(_contentManager); - return View(shapeViewModel); + // We prepare the content items SummaryAdmin shape. + var contentItemSummaries = new List(); + foreach (var contentItem in pageOfContentItems) + { + contentItemSummaries.Add(await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this, "SummaryAdmin")); } - [HttpPost] - [ActionName(nameof(List))] - [FormValueRequired("submit.Filter")] - public async Task ListFilterPOST(ContentOptionsViewModel options) + // Populate options pager summary values. + var startIndex = (pager.Page - 1) * pager.PageSize + 1; + options.StartIndex = startIndex; + options.EndIndex = startIndex + contentItemSummaries.Count - 1; + options.ContentItemsCount = contentItemSummaries.Count; + options.TotalItemCount = itemsPerPage; + + var header = await _contentOptionsDisplayManager.BuildEditorAsync(options, this, false, string.Empty, string.Empty); + + var shapeViewModel = await shapeFactory.CreateAsync("ContentsAdminList", viewModel => + { + viewModel.ContentItems = contentItemSummaries; + viewModel.Pager = pagerShape; + viewModel.Options = options; + viewModel.Header = header; + }); + + return View(shapeViewModel); + } + + [HttpPost] + [ActionName(nameof(List))] + [FormValueRequired("submit.Filter")] + public async Task ListFilterPOST(ContentOptionsViewModel options) + { + // When the user has typed something into the search input no further evaluation of the form post is required. + if (!string.Equals(options.SearchText, options.OriginalSearchText, StringComparison.OrdinalIgnoreCase)) { - // When the user has typed something into the search input no further evaluation of the form post is required. - if (!string.Equals(options.SearchText, options.OriginalSearchText, StringComparison.OrdinalIgnoreCase)) + return RedirectToAction(nameof(List), new RouteValueDictionary { - return RedirectToAction(nameof(List), new RouteValueDictionary - { - { "q", options.SearchText }, - }); - } + { "q", options.SearchText }, + }); + } - // Evaluate the values provided in the form post and map them to the filter result and route values. - await _contentOptionsDisplayManager.UpdateEditorAsync(options, this, false, string.Empty, string.Empty); + // Evaluate the values provided in the form post and map them to the filter result and route values. + await _contentOptionsDisplayManager.UpdateEditorAsync(options, this, false, string.Empty, string.Empty); - // The route value must always be added after the editors have updated the models. - options.RouteValues.TryAdd("q", options.FilterResult.ToString()); + // The route value must always be added after the editors have updated the models. + options.RouteValues.TryAdd("q", options.FilterResult.ToString()); - return RedirectToAction(nameof(List), options.RouteValues); - } + return RedirectToAction(nameof(List), options.RouteValues); + } - [HttpPost] - [ActionName(nameof(List))] - [FormValueRequired("submit.BulkAction")] - public async Task ListPOST(ContentOptionsViewModel options, long[] itemIds) + [HttpPost] + [ActionName(nameof(List))] + [FormValueRequired("submit.BulkAction")] + public async Task ListPOST(ContentOptionsViewModel options, long[] itemIds) + { + if (itemIds.Length > 0) { - if (itemIds.Length > 0) + // Load items so that loading handlers are invoked. + var checkedContentItems = await _session.Query() + .Where(x => x.DocumentId.IsIn(itemIds) && x.Latest) + .ListAsync(_contentManager); + + switch (options.BulkAction) { - // Load items so that loading handlers are invoked. - var checkedContentItems = await _session.Query() - .Where(x => x.DocumentId.IsIn(itemIds) && x.Latest) - .ListAsync(_contentManager); - - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.PublishNow: - foreach (var item in checkedContentItems) + case ContentsBulkAction.None: + break; + case ContentsBulkAction.PublishNow: + foreach (var item in checkedContentItems) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, item)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, item)) - { - await _notifier.WarningAsync(H["Couldn't publish selected content."]); - await _session.CancelAsync(); - return Forbid(); - } - - await _contentManager.PublishAsync(item); + await _notifier.WarningAsync(H["Couldn't publish selected content."]); + await _session.CancelAsync(); + return Forbid(); } - await _notifier.SuccessAsync(H["Content published successfully."]); - break; - case ContentsBulkAction.Unpublish: - foreach (var item in checkedContentItems) + + await _contentManager.PublishAsync(item); + } + await _notifier.SuccessAsync(H["Content published successfully."]); + break; + case ContentsBulkAction.Unpublish: + foreach (var item in checkedContentItems) + { + if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, item)) { - if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, item)) - { - await _notifier.WarningAsync(H["Couldn't unpublish selected content."]); - await _session.CancelAsync(); - return Forbid(); - } - - await _contentManager.UnpublishAsync(item); + await _notifier.WarningAsync(H["Couldn't unpublish selected content."]); + await _session.CancelAsync(); + return Forbid(); } - await _notifier.SuccessAsync(H["Content unpublished successfully."]); - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) + + await _contentManager.UnpublishAsync(item); + } + await _notifier.SuccessAsync(H["Content unpublished successfully."]); + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, item)) { - if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, item)) - { - await _notifier.WarningAsync(H["Couldn't remove selected content."]); - await _session.CancelAsync(); - return Forbid(); - } - - await _contentManager.RemoveAsync(item); + await _notifier.WarningAsync(H["Couldn't remove selected content."]); + await _session.CancelAsync(); + return Forbid(); } - await _notifier.SuccessAsync(H["Content removed successfully."]); - break; - default: - return BadRequest(); - } - } - - return RedirectToAction(nameof(List)); - } - - [Admin("Contents/ContentTypes/{id}/Create", "CreateContentItem")] - public async Task Create(string id) - { - if (string.IsNullOrWhiteSpace(id)) - { - return NotFound(); - } - - var contentItem = await CreateContentItemOwnedByCurrentUserAsync(id); - if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) - { - return Forbid(); + await _contentManager.RemoveAsync(item); + } + await _notifier.SuccessAsync(H["Content removed successfully."]); + break; + default: + return BadRequest(); } + } - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, this, true); + return RedirectToAction(nameof(List)); + } - return View(model); + [Admin("Contents/ContentTypes/{id}/Create", "CreateContentItem")] + public async Task Create(string id) + { + if (string.IsNullOrWhiteSpace(id)) + { + return NotFound(); } - [HttpPost] - [ActionName(nameof(Create))] - [FormValueRequired("submit.Save")] - public Task CreatePOST( - string id, - [Bind(Prefix = "submit.Save")] string submitSave, - string returnUrl) + var contentItem = await CreateContentItemOwnedByCurrentUserAsync(id); + + if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) { - var stayOnSamePage = submitSave == "submit.SaveAndContinue"; - return CreatePOST(id, returnUrl, stayOnSamePage, async contentItem => - { - await _contentManager.SaveDraftAsync(contentItem); + return Forbid(); + } - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, this, true); - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) - ? H["Your content draft has been saved."] - : H["Your {0} draft has been saved.", typeDefinition.DisplayName]); - }); - } + return View(model); + } - [HttpPost] - [ActionName(nameof(Create))] - [FormValueRequired("submit.Publish")] - public async Task CreateAndPublishPOST( - string id, - [Bind(Prefix = "submit.Publish")] string submitPublish, - string returnUrl) + [HttpPost] + [ActionName(nameof(Create))] + [FormValueRequired("submit.Save")] + public Task CreatePOST( + string id, + [Bind(Prefix = "submit.Save")] string submitSave, + string returnUrl) + { + var stayOnSamePage = submitSave == "submit.SaveAndContinue"; + return CreatePOST(id, returnUrl, stayOnSamePage, async contentItem => { - if (string.IsNullOrEmpty(id)) - { - return NotFound(); - } + await _contentManager.SaveDraftAsync(contentItem); - var stayOnSamePage = submitPublish == "submit.PublishAndContinue"; - // Pass a dummy content item to the authorization check to check for "own" variations permissions. - if (!await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.PublishContent, id, CurrentUserId())) - { - return Forbid(); - } + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - return await CreatePOST(id, returnUrl, stayOnSamePage, async contentItem => - { - await _contentManager.PublishAsync(contentItem); + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) + ? H["Your content draft has been saved."] + : H["Your {0} draft has been saved.", typeDefinition.DisplayName]); + }); + } - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + [HttpPost] + [ActionName(nameof(Create))] + [FormValueRequired("submit.Publish")] + public async Task CreateAndPublishPOST( + string id, + [Bind(Prefix = "submit.Publish")] string submitPublish, + string returnUrl) + { + if (string.IsNullOrEmpty(id)) + { + return NotFound(); + } - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition.DisplayName) - ? H["Your content has been published."] - : H["Your {0} has been published.", typeDefinition.DisplayName]); - }); + var stayOnSamePage = submitPublish == "submit.PublishAndContinue"; + // Pass a dummy content item to the authorization check to check for "own" variations permissions. + if (!await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.PublishContent, id, CurrentUserId())) + { + return Forbid(); } - [Admin("Contents/ContentItems/{contentItemId}/Display", "AdminContentItem")] - public async Task Display(string contentItemId) + return await CreatePOST(id, returnUrl, stayOnSamePage, async contentItem => { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + await _contentManager.PublishAsync(contentItem); - if (contentItem == null) - { - return NotFound(); - } + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - if (!await IsAuthorizedAsync(CommonPermissions.ViewContent, contentItem)) - { - return Forbid(); - } + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition.DisplayName) + ? H["Your content has been published."] + : H["Your {0} has been published.", typeDefinition.DisplayName]); + }); + } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this, "DetailAdmin"); + [Admin("Contents/ContentItems/{contentItemId}/Display", "AdminContentItem")] + public async Task Display(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - return View(model); + if (contentItem == null) + { + return NotFound(); } - [Admin("Contents/ContentItems/{contentItemId}/Edit", "EditContentItem")] - public async Task Edit(string contentItemId, string returnUrl = null) + if (!await IsAuthorizedAsync(CommonPermissions.ViewContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + return Forbid(); + } - if (contentItem == null) - { - return NotFound(); - } + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this, "DetailAdmin"); - if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + return View(model); + } - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, this, false); + [Admin("Contents/ContentItems/{contentItemId}/Edit", "EditContentItem")] + public async Task Edit(string contentItemId, string returnUrl = null) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - return View(model); + if (contentItem == null) + { + return NotFound(); } - [HttpPost] - [ActionName(nameof(Edit))] - [FormValueRequired("submit.Save")] - public Task EditPOST( - string contentItemId, - [Bind(Prefix = "submit.Save")] string submitSave, - string returnUrl) + if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) { - var stayOnSamePage = submitSave == "submit.SaveAndContinue"; - return EditPOST(contentItemId, returnUrl, stayOnSamePage, async contentItem => - { - await _contentManager.SaveDraftAsync(contentItem); + return Forbid(); + } - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, this, false); - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) - ? H["Your content draft has been saved."] - : H["Your {0} draft has been saved.", typeDefinition.DisplayName]); - }); - } + return View(model); + } - [HttpPost] - [ActionName(nameof(Edit))] - [FormValueRequired("submit.Publish")] - public async Task EditAndPublishPOST( - string contentItemId, - [Bind(Prefix = "submit.Publish")] string submitPublish, - string returnUrl) + [HttpPost] + [ActionName(nameof(Edit))] + [FormValueRequired("submit.Save")] + public Task EditPOST( + string contentItemId, + [Bind(Prefix = "submit.Save")] string submitSave, + string returnUrl) + { + var stayOnSamePage = submitSave == "submit.SaveAndContinue"; + return EditPOST(contentItemId, returnUrl, stayOnSamePage, async contentItem => { - var stayOnSamePage = submitPublish == "submit.PublishAndContinue"; + await _contentManager.SaveDraftAsync(contentItem); - var content = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - if (content == null) - { - return NotFound(); - } + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) + ? H["Your content draft has been saved."] + : H["Your {0} draft has been saved.", typeDefinition.DisplayName]); + }); + } - if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, content)) - { - return Forbid(); - } + [HttpPost] + [ActionName(nameof(Edit))] + [FormValueRequired("submit.Publish")] + public async Task EditAndPublishPOST( + string contentItemId, + [Bind(Prefix = "submit.Publish")] string submitPublish, + string returnUrl) + { + var stayOnSamePage = submitPublish == "submit.PublishAndContinue"; - return await EditPOST(contentItemId, returnUrl, stayOnSamePage, async contentItem => - { - await _contentManager.PublishAsync(contentItem); + var content = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + if (content == null) + { + return NotFound(); + } - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) - ? H["Your content has been published."] - : H["Your {0} has been published.", typeDefinition.DisplayName]); - }); + if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, content)) + { + return Forbid(); } - [HttpPost] - [Admin("Contents/ContentItems/{contentItemId}/Clone", "AdminCloneContentItem")] - public async Task Clone(string contentItemId, string returnUrl) + return await EditPOST(contentItemId, returnUrl, stayOnSamePage, async contentItem => { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + await _contentManager.PublishAsync(contentItem); - if (contentItem == null) - { - return NotFound(); - } + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - if (!await IsAuthorizedAsync(CommonPermissions.CloneContent, contentItem)) - { - return Forbid(); - } + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) + ? H["Your content has been published."] + : H["Your {0} has been published.", typeDefinition.DisplayName]); + }); + } - try - { - await _contentManager.CloneAsync(contentItem); - } - catch (InvalidOperationException) - { - await _notifier.WarningAsync(H["Could not clone the content item."]); - return Url.IsLocalUrl(returnUrl) - ? this.LocalRedirect(returnUrl, true) - : RedirectToAction(nameof(List)); - } + [HttpPost] + [Admin("Contents/ContentItems/{contentItemId}/Clone", "AdminCloneContentItem")] + public async Task Clone(string contentItemId, string returnUrl) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - await _notifier.InformationAsync(H["Successfully cloned. The clone was saved as a draft."]); + if (contentItem == null) + { + return NotFound(); + } + + if (!await IsAuthorizedAsync(CommonPermissions.CloneContent, contentItem)) + { + return Forbid(); + } + try + { + await _contentManager.CloneAsync(contentItem); + } + catch (InvalidOperationException) + { + await _notifier.WarningAsync(H["Could not clone the content item."]); return Url.IsLocalUrl(returnUrl) ? this.LocalRedirect(returnUrl, true) : RedirectToAction(nameof(List)); } - [HttpPost] - [Admin("Contents/ContentItems/{contentItemId}/DiscardDraft", "AdminDiscardDraftContentItem")] - public async Task DiscardDraft(string contentItemId, string returnUrl) - { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + await _notifier.InformationAsync(H["Successfully cloned. The clone was saved as a draft."]); - if (contentItem == null || contentItem.Published) - { - return NotFound(); - } + return Url.IsLocalUrl(returnUrl) + ? this.LocalRedirect(returnUrl, true) + : RedirectToAction(nameof(List)); + } - if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, contentItem)) - { - return Forbid(); - } + [HttpPost] + [Admin("Contents/ContentItems/{contentItemId}/DiscardDraft", "AdminDiscardDraftContentItem")] + public async Task DiscardDraft(string contentItemId, string returnUrl) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - if (contentItem != null) - { - await _contentManager.DiscardDraftAsync(contentItem); + if (contentItem == null || contentItem.Published) + { + return NotFound(); + } - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, contentItem)) + { + return Forbid(); + } - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) - ? H["The draft has been removed."] - : H["The {0} draft has been removed.", typeDefinition.DisplayName]); - } + if (contentItem != null) + { + await _contentManager.DiscardDraftAsync(contentItem); - return Url.IsLocalUrl(returnUrl) - ? this.LocalRedirect(returnUrl, true) - : RedirectToAction(nameof(List)); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) + ? H["The draft has been removed."] + : H["The {0} draft has been removed.", typeDefinition.DisplayName]); } - [HttpPost] - [Admin("Contents/ContentItems/{contentItemId}/Delete", "AdminDeleteContentItem")] - public async Task Remove(string contentItemId, string returnUrl) + return Url.IsLocalUrl(returnUrl) + ? this.LocalRedirect(returnUrl, true) + : RedirectToAction(nameof(List)); + } + + [HttpPost] + [Admin("Contents/ContentItems/{contentItemId}/Delete", "AdminDeleteContentItem")] + public async Task Remove(string contentItemId, string returnUrl) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + + if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + return Forbid(); + } - if (!await IsAuthorizedAsync(CommonPermissions.DeleteContent, contentItem)) - { - return Forbid(); - } + if (contentItem != null) + { + await _contentManager.RemoveAsync(contentItem); - if (contentItem != null) - { - await _contentManager.RemoveAsync(contentItem); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) + ? H["That content has been removed."] + : H["That {0} has been removed.", typeDefinition.DisplayName]); + } - await _notifier.SuccessAsync(string.IsNullOrWhiteSpace(typeDefinition?.DisplayName) - ? H["That content has been removed."] - : H["That {0} has been removed.", typeDefinition.DisplayName]); - } + return Url.IsLocalUrl(returnUrl) + ? this.LocalRedirect(returnUrl, true) + : RedirectToAction(nameof(List)); + } - return Url.IsLocalUrl(returnUrl) - ? this.LocalRedirect(returnUrl, true) - : RedirectToAction(nameof(List)); + [HttpPost] + [Admin("Contents/ContentItems/{contentItemId}/Publish", "AdminPublishContentItem")] + public async Task Publish(string contentItemId, string returnUrl) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + if (contentItem == null) + { + return NotFound(); } - [HttpPost] - [Admin("Contents/ContentItems/{contentItemId}/Publish", "AdminPublishContentItem")] - public async Task Publish(string contentItemId, string returnUrl) + if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - if (contentItem == null) - { - return NotFound(); - } + return Forbid(); + } - if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, contentItem)) - { - return Forbid(); - } + await _contentManager.PublishAsync(contentItem); - await _contentManager.PublishAsync(contentItem); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + if (string.IsNullOrEmpty(typeDefinition?.DisplayName)) + { + await _notifier.SuccessAsync(H["That content has been published."]); + } + else + { + await _notifier.SuccessAsync(H["That {0} has been published.", typeDefinition.DisplayName]); + } - if (string.IsNullOrEmpty(typeDefinition?.DisplayName)) - { - await _notifier.SuccessAsync(H["That content has been published."]); - } - else - { - await _notifier.SuccessAsync(H["That {0} has been published.", typeDefinition.DisplayName]); - } + return Url.IsLocalUrl(returnUrl) + ? this.LocalRedirect(returnUrl, true) + : RedirectToAction(nameof(List)); + } - return Url.IsLocalUrl(returnUrl) - ? this.LocalRedirect(returnUrl, true) - : RedirectToAction(nameof(List)); + [HttpPost] + [Admin("Contents/ContentItems/{contentItemId}/Unpublish", "AdminUnpublishContentItem")] + public async Task Unpublish(string contentItemId, string returnUrl) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + if (contentItem == null) + { + return NotFound(); } - [HttpPost] - [Admin("Contents/ContentItems/{contentItemId}/Unpublish", "AdminUnpublishContentItem")] - public async Task Unpublish(string contentItemId, string returnUrl) + if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - if (contentItem == null) - { - return NotFound(); - } + return Forbid(); + } - if (!await IsAuthorizedAsync(CommonPermissions.PublishContent, contentItem)) - { - return Forbid(); - } + await _contentManager.UnpublishAsync(contentItem); - await _contentManager.UnpublishAsync(contentItem); + var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - var typeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + if (string.IsNullOrEmpty(typeDefinition?.DisplayName)) + { + await _notifier.SuccessAsync(H["The content has been unpublished."]); + } + else + { + await _notifier.SuccessAsync(H["The {0} has been unpublished.", typeDefinition.DisplayName]); + } - if (string.IsNullOrEmpty(typeDefinition?.DisplayName)) - { - await _notifier.SuccessAsync(H["The content has been unpublished."]); - } - else - { - await _notifier.SuccessAsync(H["The {0} has been unpublished.", typeDefinition.DisplayName]); - } + return Url.IsLocalUrl(returnUrl) + ? this.LocalRedirect(returnUrl, true) + : RedirectToAction(nameof(List)); + } - return Url.IsLocalUrl(returnUrl) - ? this.LocalRedirect(returnUrl, true) - : RedirectToAction(nameof(List)); - } + private async Task CreatePOST( + string id, + string returnUrl, + bool stayOnSamePage, + Func conditionallyPublish) + { + var contentItem = await CreateContentItemOwnedByCurrentUserAsync(id); - private async Task CreatePOST( - string id, - string returnUrl, - bool stayOnSamePage, - Func conditionallyPublish) + if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) { - var contentItem = await CreateContentItemOwnedByCurrentUserAsync(id); + return Forbid(); + } - if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, this, true); - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, this, true); + if (ModelState.IsValid) + { + await _contentManager.CreateAsync(contentItem, VersionOptions.Draft); + } - if (ModelState.IsValid) - { - await _contentManager.CreateAsync(contentItem, VersionOptions.Draft); - } + if (!ModelState.IsValid) + { + await _session.CancelAsync(); + return View(model); + } - if (!ModelState.IsValid) - { - await _session.CancelAsync(); - return View(model); - } + await conditionallyPublish(contentItem); - await conditionallyPublish(contentItem); + if (!string.IsNullOrEmpty(returnUrl) && !stayOnSamePage) + { + return this.LocalRedirect(returnUrl, true); + } - if (!string.IsNullOrEmpty(returnUrl) && !stayOnSamePage) - { - return this.LocalRedirect(returnUrl, true); - } + var adminRouteValues = (await _contentManager.PopulateAspectAsync(contentItem)).AdminRouteValues; + + if (!string.IsNullOrEmpty(returnUrl)) + { + adminRouteValues.Add("returnUrl", returnUrl); + } - var adminRouteValues = (await _contentManager.PopulateAspectAsync(contentItem)).AdminRouteValues; + return RedirectToRoute(adminRouteValues); + } - if (!string.IsNullOrEmpty(returnUrl)) - { - adminRouteValues.Add("returnUrl", returnUrl); - } + private async Task EditPOST( + string contentItemId, + string returnUrl, + bool stayOnSamePage, + Func conditionallyPublish) + { + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.DraftRequired); - return RedirectToRoute(adminRouteValues); + if (contentItem == null) + { + return NotFound(); } - private async Task EditPOST( - string contentItemId, - string returnUrl, - bool stayOnSamePage, - Func conditionallyPublish) + if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.DraftRequired); + return Forbid(); + } - if (contentItem == null) - { - return NotFound(); - } + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, this, false); - if (!await IsAuthorizedAsync(CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + if (!ModelState.IsValid) + { + await _session.CancelAsync(); + return View(nameof(Edit), model); + } - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, this, false); + await conditionallyPublish(contentItem); - if (!ModelState.IsValid) + if (returnUrl == null) + { + return RedirectToAction(nameof(Edit), new RouteValueDictionary { - await _session.CancelAsync(); - return View(nameof(Edit), model); - } - - await conditionallyPublish(contentItem); + { "ContentItemId", contentItem.ContentItemId }, + }); + } - if (returnUrl == null) + if (stayOnSamePage) + { + return RedirectToAction(nameof(Edit), new RouteValueDictionary { - return RedirectToAction(nameof(Edit), new RouteValueDictionary - { - { "ContentItemId", contentItem.ContentItemId }, - }); - } + { "ContentItemId", contentItem.ContentItemId }, + { "returnUrl", returnUrl }, + }); + } - if (stayOnSamePage) - { - return RedirectToAction(nameof(Edit), new RouteValueDictionary - { - { "ContentItemId", contentItem.ContentItemId }, - { "returnUrl", returnUrl }, - }); - } + return this.LocalRedirect(returnUrl, true); + } - return this.LocalRedirect(returnUrl, true); - } + private async Task> GetCreatableTypeOptionsAsync( + bool canCreateSelectedContentType, + params ContentTypeDefinition[] contentTypeDefinitions) + { + var options = new List(); - private async Task> GetCreatableTypeOptionsAsync( - bool canCreateSelectedContentType, - params ContentTypeDefinition[] contentTypeDefinitions) + foreach (var contentTypeDefinition in contentTypeDefinitions) { - var options = new List(); + // Allows non creatable types to be created by another admin page. + var creatable = contentTypeDefinition.IsCreatable() || canCreateSelectedContentType; - foreach (var contentTypeDefinition in contentTypeDefinitions) + if (creatable && await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, contentTypeDefinition, CurrentUserId())) { - // Allows non creatable types to be created by another admin page. - var creatable = contentTypeDefinition.IsCreatable() || canCreateSelectedContentType; - - if (creatable && await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.EditContent, contentTypeDefinition, CurrentUserId())) - { - // Populate the creatable types. - options.Add(new SelectListItem(contentTypeDefinition.DisplayName, contentTypeDefinition.Name)); - } + // Populate the creatable types. + options.Add(new SelectListItem(contentTypeDefinition.DisplayName, contentTypeDefinition.Name)); } - - return options; } - private async Task> GetListableContentTypeOptionsAsync( - IEnumerable definitions, - string selectedContentType, - bool showSelectAll = true) - { - var currentUserId = CurrentUserId(); + return options; + } - var items = new List(); + private async Task> GetListableContentTypeOptionsAsync( + IEnumerable definitions, + string selectedContentType, + bool showSelectAll = true) + { + var currentUserId = CurrentUserId(); - if (showSelectAll) - { - items.Add(new SelectListItem(S["All content types"], string.Empty)); - }; + var items = new List(); - foreach (var definition in definitions) - { - if (!definition.IsListable() - || !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.ListContent, definition.Name, currentUserId)) - { - continue; - } + if (showSelectAll) + { + items.Add(new SelectListItem(S["All content types"], string.Empty)); + }; - items.Add(new SelectListItem(definition.DisplayName, definition.Name, string.Equals(definition.Name, selectedContentType, StringComparison.Ordinal))); + foreach (var definition in definitions) + { + if (!definition.IsListable() + || !await _authorizationService.AuthorizeContentTypeAsync(User, CommonPermissions.ListContent, definition.Name, currentUserId)) + { + continue; } - return items; + items.Add(new SelectListItem(definition.DisplayName, definition.Name, string.Equals(definition.Name, selectedContentType, StringComparison.Ordinal))); } - private async Task CreateContentItemOwnedByCurrentUserAsync(string contentType) - { - var contentItem = await _contentManager.NewAsync(contentType); - contentItem.Owner = CurrentUserId(); + return items; + } - return contentItem; - } + private async Task CreateContentItemOwnedByCurrentUserAsync(string contentType) + { + var contentItem = await _contentManager.NewAsync(contentType); + contentItem.Owner = CurrentUserId(); + + return contentItem; + } - private string _currentUserId; + private string _currentUserId; - private string CurrentUserId() - => _currentUserId ??= User.FindFirstValue(ClaimTypes.NameIdentifier); + private string CurrentUserId() + => _currentUserId ??= User.FindFirstValue(ClaimTypes.NameIdentifier); - private async Task IsAuthorizedAsync(Permission permission) - => await _authorizationService.AuthorizeAsync(User, permission); + private async Task IsAuthorizedAsync(Permission permission) + => await _authorizationService.AuthorizeAsync(User, permission); - private async Task IsAuthorizedAsync(Permission permission, object resource) - => await _authorizationService.AuthorizeAsync(User, permission, resource); - } + private async Task IsAuthorizedAsync(Permission permission, object resource) + => await _authorizationService.AuthorizeAsync(User, permission, resource); } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/ItemController.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/ItemController.cs index 66f5aaeb5c5..0465be29eab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/ItemController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Controllers/ItemController.cs @@ -5,67 +5,66 @@ using OrchardCore.ContentManagement.Display; using OrchardCore.DisplayManagement.ModelBinding; -namespace OrchardCore.Contents.Controllers +namespace OrchardCore.Contents.Controllers; + +public class ItemController : Controller, IUpdateModel { - public class ItemController : Controller, IUpdateModel + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IAuthorizationService _authorizationService; + + public ItemController( + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + IAuthorizationService authorizationService) + { + _contentManager = contentManager; + _contentItemDisplayManager = contentItemDisplayManager; + _authorizationService = authorizationService; + } + + public async Task Display(string contentItemId, string jsonPath) { - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IAuthorizationService _authorizationService; + var contentItem = await _contentManager.GetAsync(contentItemId, jsonPath); - public ItemController( - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - IAuthorizationService authorizationService) + if (contentItem == null) { - _contentManager = contentManager; - _contentItemDisplayManager = contentItemDisplayManager; - _authorizationService = authorizationService; + return NotFound(); } - public async Task Display(string contentItemId, string jsonPath) + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId, jsonPath); - - if (contentItem == null) - { - return NotFound(); - } - - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, contentItem)) - { - return this.ChallengeOrForbid(); - } + return this.ChallengeOrForbid(); + } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this); + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this); - return View(model); - } + return View(model); + } - public async Task Preview(string contentItemId) + public async Task Preview(string contentItemId) + { + if (contentItemId == null) { - if (contentItemId == null) - { - return NotFound(); - } + return NotFound(); + } - var versionOptions = VersionOptions.Latest; + var versionOptions = VersionOptions.Latest; - var contentItem = await _contentManager.GetAsync(contentItemId, versionOptions); + var contentItem = await _contentManager.GetAsync(contentItemId, versionOptions); - if (contentItem == null) - { - return NotFound(); - } + if (contentItem == null) + { + return NotFound(); + } - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PreviewContent, contentItem)) - { - return this.ChallengeOrForbid(); - } + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PreviewContent, contentItem)) + { + return this.ChallengeOrForbid(); + } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this); + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, this); - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentDriver.cs index 0dd98c0b9cf..e6f03b2643d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentDriver.cs @@ -6,27 +6,26 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +public sealed class AddToDeploymentPlanContentDriver : ContentDisplayDriver { - public sealed class AddToDeploymentPlanContentDriver : ContentDisplayDriver - { - private readonly IDeploymentPlanService _deploymentPlanService; + private readonly IDeploymentPlanService _deploymentPlanService; - public AddToDeploymentPlanContentDriver(IDeploymentPlanService deploymentPlanService) - { - _deploymentPlanService = deploymentPlanService; - } + public AddToDeploymentPlanContentDriver(IDeploymentPlanService deploymentPlanService) + { + _deploymentPlanService = deploymentPlanService; + } - public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) - { - return CombineAsync( - Dynamic("AddToDeploymentPlan_Modal__ActionDeploymentPlan") - .Location("SummaryAdmin", "ActionsMenu:30") - .RenderWhen(async () => await _deploymentPlanService.DoesUserHavePermissionsAsync()), - Shape("AddToDeploymentPlan_SummaryAdmin__Button__Actions", new ContentItemViewModel(model)) - .Location("SummaryAdmin", "ActionsMenu:30") - .RenderWhen(async () => await _deploymentPlanService.DoesUserHavePermissionsAsync()) - ); - } + public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) + { + return CombineAsync( + Dynamic("AddToDeploymentPlan_Modal__ActionDeploymentPlan") + .Location("SummaryAdmin", "ActionsMenu:30") + .RenderWhen(async () => await _deploymentPlanService.DoesUserHavePermissionsAsync()), + Shape("AddToDeploymentPlan_SummaryAdmin__Button__Actions", new ContentItemViewModel(model)) + .Location("SummaryAdmin", "ActionsMenu:30") + .RenderWhen(async () => await _deploymentPlanService.DoesUserHavePermissionsAsync()) + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentsAdminListDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentsAdminListDisplayDriver.cs index e0de100fab8..adab1037e88 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentsAdminListDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanContentsAdminListDisplayDriver.cs @@ -4,28 +4,27 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +public sealed class AddToDeploymentPlanContentsAdminListDisplayDriver : DisplayDriver { - public sealed class AddToDeploymentPlanContentsAdminListDisplayDriver : DisplayDriver + private readonly IDeploymentPlanService _deploymentPlanService; + + public AddToDeploymentPlanContentsAdminListDisplayDriver(IDeploymentPlanService deploymentPlanService) { - private readonly IDeploymentPlanService _deploymentPlanService; + _deploymentPlanService = deploymentPlanService; + } - public AddToDeploymentPlanContentsAdminListDisplayDriver(IDeploymentPlanService deploymentPlanService) + public override async Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) + { + if (await _deploymentPlanService.DoesUserHavePermissionsAsync()) { - _deploymentPlanService = deploymentPlanService; + return Combine( + Dynamic("AddToDeploymentPlan__Button__ContentsBulkActions").Location("BulkActions", "Content:20"), + Dynamic("AddToDeploymentPlan_Modal__ContentsBulkActionsDeploymentPlan").Location("BulkActions", "Content:20") + ); } - public override async Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) - { - if (await _deploymentPlanService.DoesUserHavePermissionsAsync()) - { - return Combine( - Dynamic("AddToDeploymentPlan__Button__ContentsBulkActions").Location("BulkActions", "Content:20"), - Dynamic("AddToDeploymentPlan_Modal__ContentsBulkActionsDeploymentPlan").Location("BulkActions", "Content:20") - ); - } - - return null; - } + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanController.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanController.cs index a156987b30e..bce8538412c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanController.cs @@ -13,132 +13,131 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +[Feature("OrchardCore.Contents.Deployment.AddToDeploymentPlan")] +[Admin("AddToDeploymentPlan/{action}/{deploymentPlanId}", AdminAttribute.NameFromControllerAndAction)] +public class AddToDeploymentPlanController : Controller { - [Feature("OrchardCore.Contents.Deployment.AddToDeploymentPlan")] - [Admin("AddToDeploymentPlan/{action}/{deploymentPlanId}", AdminAttribute.NameFromControllerAndAction)] - public class AddToDeploymentPlanController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly IEnumerable _factories; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public AddToDeploymentPlanController( + IAuthorizationService authorizationService, + IContentManager contentManager, + ISession session, + IEnumerable factories, + INotifier notifier, + IHtmlLocalizer htmlLocalizer + ) + { + _authorizationService = authorizationService; + _contentManager = contentManager; + _session = session; + _factories = factories; + _notifier = notifier; + H = htmlLocalizer; + } + + [HttpPost] + public async Task AddContentItem(long deploymentPlanId, string returnUrl, string contentItemId) { - private readonly IAuthorizationService _authorizationService; - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly IEnumerable _factories; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - - public AddToDeploymentPlanController( - IAuthorizationService authorizationService, - IContentManager contentManager, - ISession session, - IEnumerable factories, - INotifier notifier, - IHtmlLocalizer htmlLocalizer - ) + if (!(await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) && + await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export) + )) { - _authorizationService = authorizationService; - _contentManager = contentManager; - _session = session; - _factories = factories; - _notifier = notifier; - H = htmlLocalizer; + return Forbid(); } - [HttpPost] - public async Task AddContentItem(long deploymentPlanId, string returnUrl, string contentItemId) - { - if (!(await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) && - await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export) - )) - { - return Forbid(); - } + var deploymentPlan = await _session.GetAsync(deploymentPlanId); - var deploymentPlan = await _session.GetAsync(deploymentPlanId); + if (deploymentPlan == null) + { + return NotFound(); + } - if (deploymentPlan == null) - { - return NotFound(); - } + var contentItem = await _contentManager.GetAsync(contentItemId); - var contentItem = await _contentManager.GetAsync(contentItemId); + if (contentItem == null) + { + return NotFound(); + } - if (contentItem == null) - { - return NotFound(); - } + // Export permission is required as the overriding permission. + // Requesting EditContent would allow custom permissions to deny access to this content item. + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); + } - // Export permission is required as the overriding permission. - // Requesting EditContent would allow custom permissions to deny access to this content item. - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + var stepFactory = _factories.FirstOrDefault(x => x.Name == nameof(ContentItemDeploymentStep)); - var stepFactory = _factories.FirstOrDefault(x => x.Name == nameof(ContentItemDeploymentStep)); + if (stepFactory == null) + { + return BadRequest(); + } - if (stepFactory == null) - { - return BadRequest(); - } + var step = (ContentItemDeploymentStep)stepFactory.Create(); - var step = (ContentItemDeploymentStep)stepFactory.Create(); + step.ContentItemId = contentItem.ContentItemId; - step.ContentItemId = contentItem.ContentItemId; + deploymentPlan.DeploymentSteps.Add(step); - deploymentPlan.DeploymentSteps.Add(step); + await _notifier.SuccessAsync(H["Content added successfully to the deployment plan."]); - await _notifier.SuccessAsync(H["Content added successfully to the deployment plan."]); + await _session.SaveAsync(deploymentPlan); - await _session.SaveAsync(deploymentPlan); + return this.LocalRedirect(returnUrl, true); + } + [HttpPost] + public async Task AddContentItems(long deploymentPlanId, string returnUrl, IEnumerable itemIds) + { + if (itemIds?.Count() == 0) + { return this.LocalRedirect(returnUrl, true); } - [HttpPost] - public async Task AddContentItems(long deploymentPlanId, string returnUrl, IEnumerable itemIds) + if (!(await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) && + await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export) + )) { - if (itemIds?.Count() == 0) - { - return this.LocalRedirect(returnUrl, true); - } - - if (!(await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) && - await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export) - )) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(deploymentPlanId); + var deploymentPlan = await _session.GetAsync(deploymentPlanId); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var contentItems = await _session.Query().Where(x => x.DocumentId.IsIn(itemIds) && x.Published).ListAsync(); + var contentItems = await _session.Query().Where(x => x.DocumentId.IsIn(itemIds) && x.Published).ListAsync(); - foreach (var item in contentItems) + foreach (var item in contentItems) + { + // Export permission is required as the overriding permission. + // Requesting EditContent would allow custom permissions to deny access to this content item. + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, item)) { - // Export permission is required as the overriding permission. - // Requesting EditContent would allow custom permissions to deny access to this content item. - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, item)) - { - await _notifier.WarningAsync(H["Couldn't add selected content to deployment plan."]); - - return Forbid(); - } - var step = (ContentItemDeploymentStep)_factories.FirstOrDefault(x => x.Name == nameof(ContentItemDeploymentStep)).Create(); - step.ContentItemId = item.ContentItemId; - - deploymentPlan.DeploymentSteps.Add(step); + await _notifier.WarningAsync(H["Couldn't add selected content to deployment plan."]); + + return Forbid(); } + var step = (ContentItemDeploymentStep)_factories.FirstOrDefault(x => x.Name == nameof(ContentItemDeploymentStep)).Create(); + step.ContentItemId = item.ContentItemId; + + deploymentPlan.DeploymentSteps.Add(step); + } - await _notifier.SuccessAsync(H["Content added successfully to the deployment plan."]); + await _notifier.SuccessAsync(H["Content added successfully to the deployment plan."]); - await _session.SaveAsync(deploymentPlan); + await _session.SaveAsync(deploymentPlan); - return this.LocalRedirect(returnUrl, true); - } + return this.LocalRedirect(returnUrl, true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanStartup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanStartup.cs index e9d9f977098..74a5891450e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/AddToDeploymentPlanStartup.cs @@ -5,16 +5,15 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.Modules; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +[Feature("OrchardCore.Contents.Deployment.AddToDeploymentPlan")] +public sealed class AddToDeploymentPlanStartup : StartupBase { - [Feature("OrchardCore.Contents.Deployment.AddToDeploymentPlan")] - public sealed class AddToDeploymentPlanStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddScoped(); - services.AddScoped, AddToDeploymentPlanContentsAdminListDisplayDriver>(); - } + services.AddDeployment(); + services.AddScoped(); + services.AddScoped, AddToDeploymentPlanContentsAdminListDisplayDriver>(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentSource.cs index ab433038e8a..7c173961ce3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentSource.cs @@ -4,50 +4,49 @@ using OrchardCore.ContentManagement; using OrchardCore.Deployment; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +public class ContentItemDeploymentSource : IDeploymentSource { - public class ContentItemDeploymentSource : IDeploymentSource + private readonly IContentManager _contentManager; + + public ContentItemDeploymentSource(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public ContentItemDeploymentSource(IContentManager contentManager) - { - _contentManager = contentManager; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var contentItemDeploymentStep = step as ContentItemDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (contentItemDeploymentStep == null || contentItemDeploymentStep.ContentItemId == null) { - var contentItemDeploymentStep = step as ContentItemDeploymentStep; - - if (contentItemDeploymentStep == null || contentItemDeploymentStep.ContentItemId == null) - { - return; - } + return; + } - var contentItem = await _contentManager.GetAsync(contentItemDeploymentStep.ContentItemId); + var contentItem = await _contentManager.GetAsync(contentItemDeploymentStep.ContentItemId); - if (contentItem == null) - { - return; - } + if (contentItem == null) + { + return; + } - var jContentItem = JObject.FromObject(contentItem); - jContentItem.Remove(nameof(ContentItem.Id)); + var jContentItem = JObject.FromObject(contentItem); + jContentItem.Remove(nameof(ContentItem.Id)); - var contentStep = result.Steps.FirstOrDefault(s => s["name"]?.ToString() == "Content"); - if (contentStep != null) - { - var data = contentStep["data"] as JsonArray; - data.Add(jContentItem); - } - else + var contentStep = result.Steps.FirstOrDefault(s => s["name"]?.ToString() == "Content"); + if (contentStep != null) + { + var data = contentStep["data"] as JsonArray; + data.Add(jContentItem); + } + else + { + result.Steps.Add(new JsonObject { - result.Steps.Add(new JsonObject - { - ["name"] = "Content", - ["data"] = new JsonArray(jContentItem), - }); - } + ["name"] = "Content", + ["data"] = new JsonArray(jContentItem), + }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStep.cs index cfe713be810..1767359558e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStep.cs @@ -1,17 +1,16 @@ using OrchardCore.Deployment; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +/// +/// Adds a content item to a . +/// +public class ContentItemDeploymentStep : DeploymentStep { - /// - /// Adds a content item to a . - /// - public class ContentItemDeploymentStep : DeploymentStep + public ContentItemDeploymentStep() { - public ContentItemDeploymentStep() - { - Name = nameof(ContentItemDeploymentStep); - } - - public string ContentItemId { get; set; } + Name = nameof(ContentItemDeploymentStep); } + + public string ContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepDriver.cs index 123596dd65b..33ad88b56a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepDriver.cs @@ -6,55 +6,54 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +public sealed class ContentItemDeploymentStepDriver : DisplayDriver { - public sealed class ContentItemDeploymentStepDriver : DisplayDriver + private readonly IContentManager _contentManager; + + internal readonly IStringLocalizer S; + + public ContentItemDeploymentStepDriver(IContentManager contentManager, + IStringLocalizer stringLocalizer) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + S = stringLocalizer; + } - internal readonly IStringLocalizer S; + public override Task DisplayAsync(ContentItemDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("ContentItemDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ContentItemDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public ContentItemDeploymentStepDriver(IContentManager contentManager, - IStringLocalizer stringLocalizer) + public override IDisplayResult Edit(ContentItemDeploymentStep step, BuildEditorContext context) + { + return Initialize("ContentItemDeploymentStep_Fields_Edit", model => { - _contentManager = contentManager; - S = stringLocalizer; - } + model.ContentItemId = step.ContentItemId; + }).Location("Content"); + } - public override Task DisplayAsync(ContentItemDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ContentItemDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ContentItemDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + public override async Task UpdateAsync(ContentItemDeploymentStep step, UpdateEditorContext context) + { + var model = new ContentItemDeploymentStepViewModel(); - public override IDisplayResult Edit(ContentItemDeploymentStep step, BuildEditorContext context) + await context.Updater.TryUpdateModelAsync(model, Prefix, x => x.ContentItemId); + var contentItem = await _contentManager.GetAsync(model.ContentItemId); + + if (contentItem == null) { - return Initialize("ContentItemDeploymentStep_Fields_Edit", model => - { - model.ContentItemId = step.ContentItemId; - }).Location("Content"); + context.Updater.ModelState.AddModelError(Prefix, nameof(step.ContentItemId), S["Your content item does not exist."]); } - - public override async Task UpdateAsync(ContentItemDeploymentStep step, UpdateEditorContext context) + else { - var model = new ContentItemDeploymentStepViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix, x => x.ContentItemId); - var contentItem = await _contentManager.GetAsync(model.ContentItemId); - - if (contentItem == null) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(step.ContentItemId), S["Your content item does not exist."]); - } - else - { - step.ContentItemId = model.ContentItemId; - } - - return Edit(step, context); + step.ContentItemId = model.ContentItemId; } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepViewModel.cs index 21d040d96c5..616cec0a295 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AddToDeploymentPlan/ContentItemDeploymentStepViewModel.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan +namespace OrchardCore.Contents.Deployment.AddToDeploymentPlan; + +public class ContentItemDeploymentStepViewModel { - public class ContentItemDeploymentStepViewModel - { - [Required] - public string ContentItemId { get; set; } - } + [Required] + public string ContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentSource.cs index 25c2c8002c1..8e2faf2bf86 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentSource.cs @@ -5,55 +5,54 @@ using OrchardCore.Deployment; using YesSql; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +public class AllContentDeploymentSource : IDeploymentSource { - public class AllContentDeploymentSource : IDeploymentSource + private readonly ISession _session; + + public AllContentDeploymentSource(ISession session) + { + _session = session; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly ISession _session; + var allContentStep = step as AllContentDeploymentStep; - public AllContentDeploymentSource(ISession session) + if (allContentStep == null) { - _session = session; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var data = new JsonArray(); + result.Steps.Add(new JsonObject { - var allContentStep = step as AllContentDeploymentStep; + ["name"] = "Content", + ["data"] = data, + }); - if (allContentStep == null) - { - return; - } + foreach (var contentItem in await _session.Query(x => x.Published).ListAsync()) + { + var objectData = JObject.FromObject(contentItem); - var data = new JsonArray(); - result.Steps.Add(new JsonObject - { - ["name"] = "Content", - ["data"] = data, - }); + // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql + objectData.Remove(nameof(ContentItem.Id)); - foreach (var contentItem in await _session.Query(x => x.Published).ListAsync()) + if (allContentStep.ExportAsSetupRecipe) { - var objectData = JObject.FromObject(contentItem); - - // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql - objectData.Remove(nameof(ContentItem.Id)); - - if (allContentStep.ExportAsSetupRecipe) - { - objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; - objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; - objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; - objectData.Remove(nameof(ContentItem.ContentItemVersionId)); - objectData.Remove(nameof(ContentItem.CreatedUtc)); - objectData.Remove(nameof(ContentItem.ModifiedUtc)); - objectData.Remove(nameof(ContentItem.PublishedUtc)); - } - - data.Add(objectData); + objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; + objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; + objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; + objectData.Remove(nameof(ContentItem.ContentItemVersionId)); + objectData.Remove(nameof(ContentItem.CreatedUtc)); + objectData.Remove(nameof(ContentItem.ModifiedUtc)); + objectData.Remove(nameof(ContentItem.PublishedUtc)); } - return; + data.Add(objectData); } + + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStep.cs index 5b6c85611db..eb66caa6577 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStep.cs @@ -1,17 +1,16 @@ using OrchardCore.Deployment; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +/// +/// Adds all content items to a . +/// +public class AllContentDeploymentStep : DeploymentStep { - /// - /// Adds all content items to a . - /// - public class AllContentDeploymentStep : DeploymentStep + public AllContentDeploymentStep() { - public AllContentDeploymentStep() - { - Name = "AllContent"; - } - - public bool ExportAsSetupRecipe { get; set; } + Name = "AllContent"; } + + public bool ExportAsSetupRecipe { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStepDriver.cs index b7da3da0e4b..88490c3c447 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/AllContentDeploymentStepDriver.cs @@ -4,32 +4,31 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +public sealed class AllContentDeploymentStepDriver : DisplayDriver { - public sealed class AllContentDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllContentDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllContentDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("AllContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("AllContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllContentDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(AllContentDeploymentStep step, BuildEditorContext context) + { + return Initialize("AllContentDeploymentStep_Fields_Edit", model => { - return Initialize("AllContentDeploymentStep_Fields_Edit", model => - { - model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; - }).Location("Content"); - } + model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; + }).Location("Content"); + } - public override async Task UpdateAsync(AllContentDeploymentStep step, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsSetupRecipe); + public override async Task UpdateAsync(AllContentDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsSetupRecipe); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentSource.cs index 02cd18cdad5..1e486d79e45 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentSource.cs @@ -6,61 +6,60 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +public class ContentDeploymentSource : IDeploymentSource { - public class ContentDeploymentSource : IDeploymentSource + private readonly ISession _session; + + public ContentDeploymentSource(ISession session) + { + _session = session; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly ISession _session; + // TODO: Batch and create separate content files in the result. + + var contentStep = step as ContentDeploymentStep; - public ContentDeploymentSource(ISession session) + if (contentStep == null) { - _session = session; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var data = new JsonArray(); + + foreach (var contentItem in await _session.Query(x => x.Published && x.ContentType.IsIn(contentStep.ContentTypes)).ListAsync()) { - // TODO: Batch and create separate content files in the result. + var objectData = JObject.FromObject(contentItem); - var contentStep = step as ContentDeploymentStep; + // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql. + objectData.Remove(nameof(ContentItem.Id)); - if (contentStep == null) + if (contentStep.ExportAsSetupRecipe) { - return; + objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; + objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; + objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; + objectData.Remove(nameof(ContentItem.ContentItemVersionId)); + objectData.Remove(nameof(ContentItem.CreatedUtc)); + objectData.Remove(nameof(ContentItem.ModifiedUtc)); + objectData.Remove(nameof(ContentItem.PublishedUtc)); } - var data = new JsonArray(); - - foreach (var contentItem in await _session.Query(x => x.Published && x.ContentType.IsIn(contentStep.ContentTypes)).ListAsync()) - { - var objectData = JObject.FromObject(contentItem); - - // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql. - objectData.Remove(nameof(ContentItem.Id)); - - if (contentStep.ExportAsSetupRecipe) - { - objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; - objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; - objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; - objectData.Remove(nameof(ContentItem.ContentItemVersionId)); - objectData.Remove(nameof(ContentItem.CreatedUtc)); - objectData.Remove(nameof(ContentItem.ModifiedUtc)); - objectData.Remove(nameof(ContentItem.PublishedUtc)); - } - - data.Add(objectData); - } + data.Add(objectData); + } - if (data.HasValues()) + if (data.HasValues()) + { + var jobj = new JsonObject { - var jobj = new JsonObject - { - ["name"] = "content", - ["data"] = data, - }; + ["name"] = "content", + ["data"] = data, + }; - result.Steps.Add(jobj); - } + result.Steps.Add(jobj); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStep.cs index 3947aba560c..2ee8502f96e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStep.cs @@ -1,18 +1,17 @@ using OrchardCore.Deployment; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +/// +/// Adds all content items of a specific type to a . +/// +public class ContentDeploymentStep : DeploymentStep { - /// - /// Adds all content items of a specific type to a . - /// - public class ContentDeploymentStep : DeploymentStep + public ContentDeploymentStep() { - public ContentDeploymentStep() - { - Name = "ContentDeploymentStep"; - } - - public string[] ContentTypes { get; set; } - public bool ExportAsSetupRecipe { get; set; } + Name = "ContentDeploymentStep"; } + + public string[] ContentTypes { get; set; } + public bool ExportAsSetupRecipe { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStepDriver.cs index 17ac9fefde6..2cf525c2e13 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ContentDeploymentStepDriver.cs @@ -4,36 +4,35 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment +namespace OrchardCore.Contents.Deployment; + +public sealed class ContentDeploymentStepDriver : DisplayDriver { - public sealed class ContentDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(ContentDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(ContentDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("ContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(ContentDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(ContentDeploymentStep step, BuildEditorContext context) + { + return Initialize("ContentDeploymentStep_Fields_Edit", model => { - return Initialize("ContentDeploymentStep_Fields_Edit", model => - { - model.ContentTypes = step.ContentTypes; - model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; - }).Location("Content"); - } + model.ContentTypes = step.ContentTypes; + model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentDeploymentStep step, UpdateEditorContext context) - { - // Initializes the value to empty otherwise the model is not updated if no type is selected. - step.ContentTypes = []; + public override async Task UpdateAsync(ContentDeploymentStep step, UpdateEditorContext context) + { + // Initializes the value to empty otherwise the model is not updated if no type is selected. + step.ContentTypes = []; - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ContentTypes, x => x.ExportAsSetupRecipe); + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ContentTypes, x => x.ExportAsSetupRecipe); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DisplayJsonContentItemViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DisplayJsonContentItemViewModel.cs index 68475a65152..85114c54240 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DisplayJsonContentItemViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DisplayJsonContentItemViewModel.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.Deployment.Download +namespace OrchardCore.Contents.Deployment.Download; + +public class DisplayJsonContentItemViewModel { - public class DisplayJsonContentItemViewModel - { - public ContentItem ContentItem { get; set; } - public string ContentItemJson { get; set; } - } + public ContentItem ContentItem { get; set; } + public string ContentItemJson { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadContentDriver.cs index d799bf59d1d..ee2b94e777f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadContentDriver.cs @@ -6,28 +6,27 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment.Download +namespace OrchardCore.Contents.Deployment.Download; + +public sealed class DownloadContentDriver : ContentDisplayDriver { - public sealed class DownloadContentDriver : ContentDisplayDriver - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; - public DownloadContentDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + public DownloadContentDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) + { + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public override IDisplayResult Display(ContentItem contentItem, BuildDisplayContext context) - { - var user = _httpContextAccessor.HttpContext.User; + public override IDisplayResult Display(ContentItem contentItem, BuildDisplayContext context) + { + var user = _httpContextAccessor.HttpContext.User; - return Shape("Download_SummaryAdmin__Button__Actions", new ContentItemViewModel(contentItem)) - .Location("SummaryAdmin", "ActionsMenu:20") - .RenderWhen(() => _authorizationService.AuthorizeAsync(user, OrchardCore.Deployment.CommonPermissions.Export, contentItem)); - } + return Shape("Download_SummaryAdmin__Button__Actions", new ContentItemViewModel(contentItem)) + .Location("SummaryAdmin", "ActionsMenu:20") + .RenderWhen(() => _authorizationService.AuthorizeAsync(user, OrchardCore.Deployment.CommonPermissions.Export, contentItem)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadController.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadController.cs index 2f1a4cf918a..67232dcda1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadController.cs @@ -7,79 +7,78 @@ using OrchardCore.ContentManagement; using OrchardCore.Modules; -namespace OrchardCore.Contents.Deployment.Download +namespace OrchardCore.Contents.Deployment.Download; + +[Admin("Download/{action}/{contentItemId}", AdminAttribute.NameFromControllerAndAction)] +[Feature("OrchardCore.Contents.Deployment.Download")] +public class DownloadController : Controller { - [Admin("Download/{action}/{contentItemId}", AdminAttribute.NameFromControllerAndAction)] - [Feature("OrchardCore.Contents.Deployment.Download")] - public class DownloadController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IContentManager _contentManager; + + public DownloadController( + IAuthorizationService authorizationService, + IContentManager contentManager) { - private readonly IAuthorizationService _authorizationService; - private readonly IContentManager _contentManager; + _authorizationService = authorizationService; + _contentManager = contentManager; + } - public DownloadController( - IAuthorizationService authorizationService, - IContentManager contentManager) + [HttpGet] + public async Task Display(string contentItemId, bool latest = false) + { + if (!await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export)) { - _authorizationService = authorizationService; - _contentManager = contentManager; + return Forbid(); } - [HttpGet] - public async Task Display(string contentItemId, bool latest = false) - { - if (!await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export)) - { - return Forbid(); - } - - var contentItem = await _contentManager.GetAsync(contentItemId, latest == false ? VersionOptions.Published : VersionOptions.Latest); - - if (contentItem == null) - { - return NotFound(); - } - - // Export permission is required as the overriding permission. - // Requesting EditContent would allow custom permissions to deny access to this content item. - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + var contentItem = await _contentManager.GetAsync(contentItemId, latest == false ? VersionOptions.Published : VersionOptions.Latest); - var model = new DisplayJsonContentItemViewModel - { - ContentItem = contentItem, - ContentItemJson = JObject.FromObject(contentItem).ToString() - }; + if (contentItem == null) + { + return NotFound(); + } - return View(model); + // Export permission is required as the overriding permission. + // Requesting EditContent would allow custom permissions to deny access to this content item. + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); } - [HttpPost] - public async Task Download(string contentItemId, bool latest = false) + var model = new DisplayJsonContentItemViewModel { - if (!await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export)) - { - return Forbid(); - } + ContentItem = contentItem, + ContentItemJson = JObject.FromObject(contentItem).ToString() + }; - var contentItem = await _contentManager.GetAsync(contentItemId, latest == false ? VersionOptions.Published : VersionOptions.Latest); + return View(model); + } - if (contentItem == null) - { - return NotFound(); - } + [HttpPost] + public async Task Download(string contentItemId, bool latest = false) + { + if (!await _authorizationService.AuthorizeAsync(User, OrchardCore.Deployment.CommonPermissions.Export)) + { + return Forbid(); + } - // Export permission is required as the overriding permission. - // Requesting EditContent would allow custom permissions to deny access to this content item. - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + var contentItem = await _contentManager.GetAsync(contentItemId, latest == false ? VersionOptions.Published : VersionOptions.Latest); - var jItem = JObject.FromObject(contentItem); + if (contentItem == null) + { + return NotFound(); + } - return File(Encoding.UTF8.GetBytes(jItem.ToString()), "application/json", $"{contentItem.ContentItemId}.json"); + // Export permission is required as the overriding permission. + // Requesting EditContent would allow custom permissions to deny access to this content item. + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.EditContent, contentItem)) + { + return Forbid(); } + + var jItem = JObject.FromObject(contentItem); + + return File(Encoding.UTF8.GetBytes(jItem.ToString()), "application/json", $"{contentItem.ContentItemId}.json"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadStartup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadStartup.cs index dfb4aa3278a..1bb2e5768eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/Download/DownloadStartup.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement.Display.ContentDisplay; using OrchardCore.Modules; -namespace OrchardCore.Contents.Deployment.Download +namespace OrchardCore.Contents.Deployment.Download; + +[Feature("OrchardCore.Contents.Deployment.Download")] +public sealed class DownloadStartup : StartupBase { - [Feature("OrchardCore.Contents.Deployment.Download")] - public sealed class DownloadStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetAdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetAdminMenu.cs index 14c6bc75687..c74fee58abb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetAdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetAdminMenu.cs @@ -3,44 +3,43 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetAdminMenu : INavigationProvider { - public sealed class ExportContentToDeploymentTargetAdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", ExportContentToDeploymentTargetSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", ExportContentToDeploymentTargetSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public ExportContentToDeploymentTargetAdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public ExportContentToDeploymentTargetAdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Import/Export"], S["Import/Export"].PrefixPosition(), import => import - .Add(S["Settings"], settings => settings - .Add(S["Export Target Settings"], S["Export Target Settings"].PrefixPosition(), targetSettings => targetSettings - .Action("Index", "Admin", _routeValues) - .Permission(OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Import/Export"], S["Import/Export"].PrefixPosition(), import => import + .Add(S["Settings"], settings => settings + .Add(S["Export Target Settings"], S["Export Target Settings"].PrefixPosition(), targetSettings => targetSettings + .Action("Index", "Admin", _routeValues) + .Permission(OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan) + .LocalNav() ) ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentDriver.cs index b7f1c02d632..3243b992e49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentDriver.cs @@ -7,57 +7,56 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetContentDriver : ContentDisplayDriver { - public sealed class ExportContentToDeploymentTargetContentDriver : ContentDisplayDriver + private readonly IDeploymentPlanService _deploymentPlanService; + private readonly ISiteService _siteService; + + public ExportContentToDeploymentTargetContentDriver( + IDeploymentPlanService deploymentPlanService, + ISiteService siteService) + { + _deploymentPlanService = deploymentPlanService; + _siteService = siteService; + } + + public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) { - private readonly IDeploymentPlanService _deploymentPlanService; - private readonly ISiteService _siteService; - - public ExportContentToDeploymentTargetContentDriver( - IDeploymentPlanService deploymentPlanService, - ISiteService siteService) - { - _deploymentPlanService = deploymentPlanService; - _siteService = siteService; - } - - public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) - { - return CombineAsync( - Dynamic("ExportContentToDeploymentTarget_Modal__ActionDeploymentTarget") - .Location("SummaryAdmin", "ActionsMenu:30") - .RenderWhen(async () => + return CombineAsync( + Dynamic("ExportContentToDeploymentTarget_Modal__ActionDeploymentTarget") + .Location("SummaryAdmin", "ActionsMenu:30") + .RenderWhen(async () => + { + if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) { - if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) - { - var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); + var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); - if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) - { - return true; - } + if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) + { + return true; } + } - return false; - }), - Shape("ExportContentToDeploymentTarget_SummaryAdmin__Button__Actions", new ContentItemViewModel(model)) - .Location("SummaryAdmin", "ActionsMenu:40") - .RenderWhen(async () => + return false; + }), + Shape("ExportContentToDeploymentTarget_SummaryAdmin__Button__Actions", new ContentItemViewModel(model)) + .Location("SummaryAdmin", "ActionsMenu:40") + .RenderWhen(async () => + { + if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) { - if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) - { - var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); + var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); - if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) - { - return true; - } + if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) + { + return true; } + } - return false; - }) - ); - } + return false; + }) + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentsAdminListDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentsAdminListDisplayDriver.cs index dc5e26c1d9e..074862e9a97 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentsAdminListDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetContentsAdminListDisplayDriver.cs @@ -5,37 +5,36 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetContentsAdminListDisplayDriver : DisplayDriver { - public sealed class ExportContentToDeploymentTargetContentsAdminListDisplayDriver : DisplayDriver + private readonly IDeploymentPlanService _deploymentPlanService; + private readonly ISiteService _siteService; + + public ExportContentToDeploymentTargetContentsAdminListDisplayDriver( + IDeploymentPlanService deploymentPlanService, + ISiteService siteService) { - private readonly IDeploymentPlanService _deploymentPlanService; - private readonly ISiteService _siteService; + _deploymentPlanService = deploymentPlanService; + _siteService = siteService; + } - public ExportContentToDeploymentTargetContentsAdminListDisplayDriver( - IDeploymentPlanService deploymentPlanService, - ISiteService siteService) + public override async Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) + { + if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) { - _deploymentPlanService = deploymentPlanService; - _siteService = siteService; - } + var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); - public override async Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) - { - if (await _deploymentPlanService.DoesUserHaveExportPermissionAsync()) + if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) { - var exportContentToDeploymentTargetSettings = await _siteService.GetSettingsAsync(); - - if (exportContentToDeploymentTargetSettings.ExportContentToDeploymentTargetPlanId != 0) - { - return Combine( - Dynamic("ExportContentToDeploymentTarget__Button__ContentsBulkActions").Location("BulkActions", "Content:30"), - Dynamic("ExportContentToDeploymentTarget_Modal__ContentsBulkActionsDeploymentTarget").Location("BulkActions", "Content:30") - ); - } + return Combine( + Dynamic("ExportContentToDeploymentTarget__Button__ContentsBulkActions").Location("BulkActions", "Content:30"), + Dynamic("ExportContentToDeploymentTarget_Modal__ContentsBulkActionsDeploymentTarget").Location("BulkActions", "Content:30") + ); } - - return null; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentSource.cs index ddc4f0d4748..a629adc253e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentSource.cs @@ -10,72 +10,71 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public class ExportContentToDeploymentTargetDeploymentSource : IDeploymentSource { - public class ExportContentToDeploymentTargetDeploymentSource : IDeploymentSource + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public ExportContentToDeploymentTargetDeploymentSource( + IContentManager contentManager, + ISession session, + IUpdateModelAccessor updateModelAccessor) + { + _contentManager = contentManager; + _session = session; + _updateModelAccessor = updateModelAccessor; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly IUpdateModelAccessor _updateModelAccessor; + var exportContentToDeploymentTargetContentDeploymentStep = step as ExportContentToDeploymentTargetDeploymentStep; - public ExportContentToDeploymentTargetDeploymentSource( - IContentManager contentManager, - ISession session, - IUpdateModelAccessor updateModelAccessor) + if (exportContentToDeploymentTargetContentDeploymentStep == null) { - _contentManager = contentManager; - _session = session; - _updateModelAccessor = updateModelAccessor; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var data = new JsonArray(); + result.Steps.Add(new JsonObject { - var exportContentToDeploymentTargetContentDeploymentStep = step as ExportContentToDeploymentTargetDeploymentStep; - - if (exportContentToDeploymentTargetContentDeploymentStep == null) - { - return; - } - - var data = new JsonArray(); - result.Steps.Add(new JsonObject - { - ["name"] = "Content", - ["data"] = data, - }); + ["name"] = "Content", + ["data"] = data, + }); - var model = new ExportContentToDeploymentTargetModel(); - await _updateModelAccessor.ModelUpdater.TryUpdateModelAsync(model, "ExportContentToDeploymentTarget", m => m.ItemIds, m => m.Latest, m => m.ContentItemId); + var model = new ExportContentToDeploymentTargetModel(); + await _updateModelAccessor.ModelUpdater.TryUpdateModelAsync(model, "ExportContentToDeploymentTarget", m => m.ItemIds, m => m.Latest, m => m.ContentItemId); - if (!string.IsNullOrEmpty(model.ContentItemId)) + if (!string.IsNullOrEmpty(model.ContentItemId)) + { + var contentItem = await _contentManager.GetAsync(model.ContentItemId, model.Latest ? VersionOptions.Latest : VersionOptions.Published); + if (contentItem != null) { - var contentItem = await _contentManager.GetAsync(model.ContentItemId, model.Latest ? VersionOptions.Latest : VersionOptions.Published); - if (contentItem != null) - { - var objectData = JObject.FromObject(contentItem); - objectData.Remove(nameof(ContentItem.Id)); - data.Add(objectData); - } + var objectData = JObject.FromObject(contentItem); + objectData.Remove(nameof(ContentItem.Id)); + data.Add(objectData); } + } - if (model.ItemIds?.Count() > 0) - { - var checkedContentItems = await _session.Query().Where(x => x.DocumentId.IsIn(model.ItemIds) && x.Published).ListAsync(); + if (model.ItemIds?.Count() > 0) + { + var checkedContentItems = await _session.Query().Where(x => x.DocumentId.IsIn(model.ItemIds) && x.Published).ListAsync(); - foreach (var contentItem in checkedContentItems) - { - var objectData = JObject.FromObject(contentItem); - objectData.Remove(nameof(ContentItem.Id)); - data.Add(objectData); - } + foreach (var contentItem in checkedContentItems) + { + var objectData = JObject.FromObject(contentItem); + objectData.Remove(nameof(ContentItem.Id)); + data.Add(objectData); } } + } - public class ExportContentToDeploymentTargetModel - { - public IEnumerable ItemIds { get; set; } - public string ContentItemId { get; set; } - public bool Latest { get; set; } - } + public class ExportContentToDeploymentTargetModel + { + public IEnumerable ItemIds { get; set; } + public string ContentItemId { get; set; } + public bool Latest { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStep.cs index 80d4d1cc7f6..dac81ca28e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +/// +/// Adds content selected with export content to deployment plan target feature to a . +/// +public class ExportContentToDeploymentTargetDeploymentStep : DeploymentStep { - /// - /// Adds content selected with export content to deployment plan target feature to a . - /// - public class ExportContentToDeploymentTargetDeploymentStep : DeploymentStep + public ExportContentToDeploymentTargetDeploymentStep() { - public ExportContentToDeploymentTargetDeploymentStep() - { - Name = nameof(ExportContentToDeploymentTargetDeploymentStep); - } + Name = nameof(ExportContentToDeploymentTargetDeploymentStep); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStepDriver.cs index 2391664e9cd..f4419370edb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetDeploymentStepDriver : DisplayDriver { - public sealed class ExportContentToDeploymentTargetDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(ExportContentToDeploymentTargetDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(ExportContentToDeploymentTargetDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ExportContentToDeploymentTargetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ExportContentToDeploymentTargetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("ExportContentToDeploymentTargetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ExportContentToDeploymentTargetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(ExportContentToDeploymentTargetDeploymentStep step, BuildEditorContext context) - { - return View("ExportContentToDeploymentTargetDeploymentStep_Fields_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(ExportContentToDeploymentTargetDeploymentStep step, BuildEditorContext context) + { + return View("ExportContentToDeploymentTargetDeploymentStep_Fields_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetMigrations.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetMigrations.cs index cb5facc89ce..00affbf2f85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetMigrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetMigrations.cs @@ -7,41 +7,40 @@ using OrchardCore.Recipes.Services; using OrchardCore.Settings; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetMigrations : DataMigration { - public sealed class ExportContentToDeploymentTargetMigrations : DataMigration - { - private readonly IRecipeMigrator _recipeMigrator; - private readonly IDeploymentPlanService _deploymentPlanService; - private readonly ISiteService _siteService; - - public ExportContentToDeploymentTargetMigrations( - IRecipeMigrator recipeMigrator, - IDeploymentPlanService deploymentPlanService, - ISiteService siteService - ) - { - _recipeMigrator = recipeMigrator; - _deploymentPlanService = deploymentPlanService; - _siteService = siteService; - } + private readonly IRecipeMigrator _recipeMigrator; + private readonly IDeploymentPlanService _deploymentPlanService; + private readonly ISiteService _siteService; - public async Task CreateAsync() - { - await _recipeMigrator.ExecuteAsync($"exportcontenttodeploymenttarget{RecipesConstants.RecipeExtension}", this); + public ExportContentToDeploymentTargetMigrations( + IRecipeMigrator recipeMigrator, + IDeploymentPlanService deploymentPlanService, + ISiteService siteService + ) + { + _recipeMigrator = recipeMigrator; + _deploymentPlanService = deploymentPlanService; + _siteService = siteService; + } - var deploymentPlans = await _deploymentPlanService.GetAllDeploymentPlansAsync(); - var exportContentToDeploymentTargetPlan = deploymentPlans.FirstOrDefault(x => x.DeploymentSteps.Any(x => x.Name == nameof(ExportContentToDeploymentTargetDeploymentStep))); + public async Task CreateAsync() + { + await _recipeMigrator.ExecuteAsync($"exportcontenttodeploymenttarget{RecipesConstants.RecipeExtension}", this); - if (exportContentToDeploymentTargetPlan != null) - { - var siteSettings = await _siteService.LoadSiteSettingsAsync(); - siteSettings.Alter(aspect => aspect.ExportContentToDeploymentTargetPlanId = exportContentToDeploymentTargetPlan.Id); + var deploymentPlans = await _deploymentPlanService.GetAllDeploymentPlansAsync(); + var exportContentToDeploymentTargetPlan = deploymentPlans.FirstOrDefault(x => x.DeploymentSteps.Any(x => x.Name == nameof(ExportContentToDeploymentTargetDeploymentStep))); - await _siteService.UpdateSiteSettingsAsync(siteSettings); - } + if (exportContentToDeploymentTargetPlan != null) + { + var siteSettings = await _siteService.LoadSiteSettingsAsync(); + siteSettings.Alter(aspect => aspect.ExportContentToDeploymentTargetPlanId = exportContentToDeploymentTargetPlan.Id); - return 1; + await _siteService.UpdateSiteSettingsAsync(siteSettings); } + + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettings.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettings.cs index 7da5706622e..43a861bdfb3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettings.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public class ExportContentToDeploymentTargetSettings { - public class ExportContentToDeploymentTargetSettings - { - /// - /// The id of the export content to deployment target deployment plan. - /// - public long ExportContentToDeploymentTargetPlanId { get; set; } - } + /// + /// The id of the export content to deployment target deployment plan. + /// + public long ExportContentToDeploymentTargetPlanId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsDisplayDriver.cs index 04d51599ea8..fc92b731db7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsDisplayDriver.cs @@ -6,50 +6,49 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public sealed class ExportContentToDeploymentTargetSettingsDisplayDriver : SiteDisplayDriver { - public sealed class ExportContentToDeploymentTargetSettingsDisplayDriver : SiteDisplayDriver - { - public const string GroupId = "ExportContentToDeploymentTarget"; + public const string GroupId = "ExportContentToDeploymentTarget"; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; - public ExportContentToDeploymentTargetSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + public ExportContentToDeploymentTargetSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) + { + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - protected override string SettingsGroupId - => GroupId; + protected override string SettingsGroupId + => GroupId; - public override async Task EditAsync(ISite site, ExportContentToDeploymentTargetSettings settings, BuildEditorContext context) + public override async Task EditAsync(ISite site, ExportContentToDeploymentTargetSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, OrchardCore.Deployment.CommonPermissions.ManageDeploymentPlan)) - { - return null; - } - - return Initialize("ExportContentToDeploymentTargetSettings_Edit", model => - { - model.ExportContentToDeploymentTargetPlanId = settings.ExportContentToDeploymentTargetPlanId; - }).Location("Content:2") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, ExportContentToDeploymentTargetSettings settings, UpdateEditorContext context) + return Initialize("ExportContentToDeploymentTargetSettings_Edit", model => { - var model = new ExportContentToDeploymentTargetSettingsViewModel(); + model.ExportContentToDeploymentTargetPlanId = settings.ExportContentToDeploymentTargetPlanId; + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.ExportContentToDeploymentTargetPlanId); + public override async Task UpdateAsync(ISite site, ExportContentToDeploymentTargetSettings settings, UpdateEditorContext context) + { + var model = new ExportContentToDeploymentTargetSettingsViewModel(); - settings.ExportContentToDeploymentTargetPlanId = model.ExportContentToDeploymentTargetPlanId; + await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.ExportContentToDeploymentTargetPlanId); - return await EditAsync(site, settings, context); - } + settings.ExportContentToDeploymentTargetPlanId = model.ExportContentToDeploymentTargetPlanId; + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsViewModel.cs index b8691911d22..d9c21404d36 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +public class ExportContentToDeploymentTargetSettingsViewModel { - public class ExportContentToDeploymentTargetSettingsViewModel - { - public long ExportContentToDeploymentTargetPlanId { get; set; } - } + public long ExportContentToDeploymentTargetPlanId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetStartup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetStartup.cs index 1de88587120..581c9a6bdb5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Deployment/ExportContentToDeploymentTarget/ExportContentToDeploymentTargetStartup.cs @@ -9,24 +9,23 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget +namespace OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget; + +[Feature("OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget")] +public sealed class ExportContentToDeploymentTargetStartup : StartupBase { - [Feature("OrchardCore.Contents.Deployment.ExportContentToDeploymentTarget")] - public sealed class ExportContentToDeploymentTargetStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); + services.AddScoped(); - services.AddScoped, ExportContentToDeploymentTargetSettingsDisplayDriver>(); + services.AddScoped, ExportContentToDeploymentTargetSettingsDisplayDriver>(); - services.AddDeployment(); + services.AddDeployment(); - services.AddDataMigration(); - services.AddScoped(); - services.AddScoped, ExportContentToDeploymentTargetContentsAdminListDisplayDriver>(); + services.AddDataMigration(); + services.AddScoped(); + services.AddScoped, ExportContentToDeploymentTargetContentsAdminListDisplayDriver>(); - services.AddSiteSettingsPropertyDeploymentStep(S => S["Export Content To Deployment Target settings"], S => S["Exports the Export Content To Deployment Target settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Export Content To Deployment Target settings"], S => S["Exports the Export Content To Deployment Target settings."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentOptionsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentOptionsDisplayDriver.cs index 434f83f3c97..7f501fc5e46 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentOptionsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentOptionsDisplayDriver.cs @@ -3,68 +3,67 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Drivers +namespace OrchardCore.Contents.Drivers; + +public sealed class ContentOptionsDisplayDriver : DisplayDriver { - public sealed class ContentOptionsDisplayDriver : DisplayDriver + // Maintain the Options prefix for compatibility with binding. + protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) { - // Maintain the Options prefix for compatibility with binding. - protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) - { - Prefix = "Options"; - } + Prefix = "Options"; + } - public override Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) - { - return CombineAsync( - Initialize("ContentsAdminListBulkActions", m => BuildContentOptionsViewModel(m, model)) - .Location("BulkActions", "Content:10"), - View("ContentsAdminFilters_Thumbnail__DisplayText", model).Location("Thumbnail", "Content:10"), - View("ContentsAdminFilters_Thumbnail__ContentType", model).Location("Thumbnail", "Content:20"), - View("ContentsAdminFilters_Thumbnail__Stereotype", model).Location("Thumbnail", "Content:30"), - View("ContentsAdminFilters_Thumbnail__Status", model).Location("Thumbnail", "Content:40"), - View("ContentsAdminFilters_Thumbnail__Sort", model).Location("Thumbnail", "Content:50") - ); - } + public override Task DisplayAsync(ContentOptionsViewModel model, BuildDisplayContext context) + { + return CombineAsync( + Initialize("ContentsAdminListBulkActions", m => BuildContentOptionsViewModel(m, model)) + .Location("BulkActions", "Content:10"), + View("ContentsAdminFilters_Thumbnail__DisplayText", model).Location("Thumbnail", "Content:10"), + View("ContentsAdminFilters_Thumbnail__ContentType", model).Location("Thumbnail", "Content:20"), + View("ContentsAdminFilters_Thumbnail__Stereotype", model).Location("Thumbnail", "Content:30"), + View("ContentsAdminFilters_Thumbnail__Status", model).Location("Thumbnail", "Content:40"), + View("ContentsAdminFilters_Thumbnail__Sort", model).Location("Thumbnail", "Content:50") + ); + } - public override Task EditAsync(ContentOptionsViewModel model, BuildEditorContext context) - { - // Map the filter result to a model so the ui can reflect current selections. - model.FilterResult.MapTo(model); + public override Task EditAsync(ContentOptionsViewModel model, BuildEditorContext context) + { + // Map the filter result to a model so the ui can reflect current selections. + model.FilterResult.MapTo(model); - return CombineAsync( - Initialize("ContentsAdminListSearch", m => BuildContentOptionsViewModel(m, model)).Location("Search:10"), - Initialize("ContentsAdminListCreate", m => BuildContentOptionsViewModel(m, model)).Location("Create:10"), - Initialize("ContentsAdminListSummary", m => BuildContentOptionsViewModel(m, model)).Location("Summary:10"), - Initialize("ContentsAdminListFilters", m => BuildContentOptionsViewModel(m, model)).Location("Actions:10.1"), - Initialize("ContentsAdminList_Fields_BulkActions", m => BuildContentOptionsViewModel(m, model)).Location("Actions:10.1") - ); - } + return CombineAsync( + Initialize("ContentsAdminListSearch", m => BuildContentOptionsViewModel(m, model)).Location("Search:10"), + Initialize("ContentsAdminListCreate", m => BuildContentOptionsViewModel(m, model)).Location("Create:10"), + Initialize("ContentsAdminListSummary", m => BuildContentOptionsViewModel(m, model)).Location("Summary:10"), + Initialize("ContentsAdminListFilters", m => BuildContentOptionsViewModel(m, model)).Location("Actions:10.1"), + Initialize("ContentsAdminList_Fields_BulkActions", m => BuildContentOptionsViewModel(m, model)).Location("Actions:10.1") + ); + } - public override Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) - { - // Map the incoming values from a form post to the filter result. - model.FilterResult.MapFrom(model); + public override Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + { + // Map the incoming values from a form post to the filter result. + model.FilterResult.MapFrom(model); - return EditAsync(model, context); - } + return EditAsync(model, context); + } - private static void BuildContentOptionsViewModel(ContentOptionsViewModel m, ContentOptionsViewModel model) - { - m.ContentTypeOptions = model.ContentTypeOptions; - m.ContentStatuses = model.ContentStatuses; - m.ContentSorts = model.ContentSorts; - m.ContentsBulkAction = model.ContentsBulkAction; - m.CreatableTypes = model.CreatableTypes; - m.StartIndex = model.StartIndex; - m.EndIndex = model.EndIndex; - m.ContentItemsCount = model.ContentItemsCount; - m.TotalItemCount = model.TotalItemCount; - m.SearchText = model.SearchText; - m.OriginalSearchText = model.OriginalSearchText; - m.ContentsStatus = model.ContentsStatus; - m.OrderBy = model.OrderBy; - m.SelectedContentType = model.SelectedContentType; - m.FilterResult = model.FilterResult; - } + private static void BuildContentOptionsViewModel(ContentOptionsViewModel m, ContentOptionsViewModel model) + { + m.ContentTypeOptions = model.ContentTypeOptions; + m.ContentStatuses = model.ContentStatuses; + m.ContentSorts = model.ContentSorts; + m.ContentsBulkAction = model.ContentsBulkAction; + m.CreatableTypes = model.CreatableTypes; + m.StartIndex = model.StartIndex; + m.EndIndex = model.EndIndex; + m.ContentItemsCount = model.ContentItemsCount; + m.TotalItemCount = model.TotalItemCount; + m.SearchText = model.SearchText; + m.OriginalSearchText = model.OriginalSearchText; + m.ContentsStatus = model.ContentsStatus; + m.OrderBy = model.OrderBy; + m.SelectedContentType = model.SelectedContentType; + m.FilterResult = model.FilterResult; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentsDriver.cs index 640949c2c14..a362ff74f82 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/ContentsDriver.cs @@ -11,105 +11,104 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Contents.Drivers +namespace OrchardCore.Contents.Drivers; + +public sealed class ContentsDriver : ContentDisplayDriver { - public sealed class ContentsDriver : ContentDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentsDriver( + IContentDefinitionManager contentDefinitionManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - - public ContentsDriver( - IContentDefinitionManager contentDefinitionManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _contentDefinitionManager = contentDefinitionManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + _contentDefinitionManager = contentDefinitionManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public override async Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) + public override async Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) + { + // We add custom alternates. This could be done generically to all shapes coming from ContentDisplayDriver but right now it's + // only necessary on this shape. Otherwise c.f. ContentPartDisplayDriver. + + var results = new List() { - // We add custom alternates. This could be done generically to all shapes coming from ContentDisplayDriver but right now it's - // only necessary on this shape. Otherwise c.f. ContentPartDisplayDriver. + Shape("ContentsTags_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Tags:10"), + Shape("ContentsMeta_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Meta:20"), + }; - var results = new List() - { - Shape("ContentsTags_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Tags:10"), - Shape("ContentsMeta_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Meta:20"), - }; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + if (contentTypeDefinition != null) + { + var contentsMetadataShape = Shape("ContentsMetadata", new ContentItemViewModel(contentItem)) + .Location("Detail", "Content:before"); - if (contentTypeDefinition != null) + contentsMetadataShape.Displaying(ctx => { - var contentsMetadataShape = Shape("ContentsMetadata", new ContentItemViewModel(contentItem)) - .Location("Detail", "Content:before"); + var hasStereotype = contentTypeDefinition.TryGetStereotype(out var stereotype); - contentsMetadataShape.Displaying(ctx => + if (hasStereotype && !string.Equals("Content", stereotype, StringComparison.OrdinalIgnoreCase)) { - var hasStereotype = contentTypeDefinition.TryGetStereotype(out var stereotype); + ctx.Shape.Metadata.Alternates.Add($"{stereotype}__ContentsMetadata"); + } - if (hasStereotype && !string.Equals("Content", stereotype, StringComparison.OrdinalIgnoreCase)) - { - ctx.Shape.Metadata.Alternates.Add($"{stereotype}__ContentsMetadata"); - } + var displayType = ctx.Shape.Metadata.DisplayType; - var displayType = ctx.Shape.Metadata.DisplayType; + if (!string.IsNullOrEmpty(displayType) && displayType != "Detail") + { + ctx.Shape.Metadata.Alternates.Add($"ContentsMetadata_{ctx.Shape.Metadata.DisplayType}"); - if (!string.IsNullOrEmpty(displayType) && displayType != "Detail") + if (hasStereotype && !string.Equals("Content", stereotype, StringComparison.OrdinalIgnoreCase)) { - ctx.Shape.Metadata.Alternates.Add($"ContentsMetadata_{ctx.Shape.Metadata.DisplayType}"); - - if (hasStereotype && !string.Equals("Content", stereotype, StringComparison.OrdinalIgnoreCase)) - { - ctx.Shape.Metadata.Alternates.Add($"{stereotype}_{displayType}__ContentsMetadata"); - } + ctx.Shape.Metadata.Alternates.Add($"{stereotype}_{displayType}__ContentsMetadata"); } - }); + } + }); - var user = _httpContextAccessor.HttpContext.User; + var user = _httpContextAccessor.HttpContext.User; - results.Add(contentsMetadataShape); - results.Add(Shape("ContentsButtonEdit_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Actions:10")); - results.Add(Shape("ContentsButtonActions_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "ActionsMenu:10") - .RenderWhen(async () => - { - var hasPublishPermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.PublishContent, contentItem); - var hasDeletePermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.DeleteContent, contentItem); - var hasPreviewPermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.PreviewContent, contentItem); - var hasClonePermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.CloneContent, contentItem); + results.Add(contentsMetadataShape); + results.Add(Shape("ContentsButtonEdit_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "Actions:10")); + results.Add(Shape("ContentsButtonActions_SummaryAdmin", new ContentItemViewModel(contentItem)).Location("SummaryAdmin", "ActionsMenu:10") + .RenderWhen(async () => + { + var hasPublishPermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.PublishContent, contentItem); + var hasDeletePermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.DeleteContent, contentItem); + var hasPreviewPermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.PreviewContent, contentItem); + var hasClonePermission = await _authorizationService.AuthorizeAsync(user, CommonPermissions.CloneContent, contentItem); + + return hasPublishPermission || hasDeletePermission || hasPreviewPermission || hasClonePermission; + }) + ); + } - return hasPublishPermission || hasDeletePermission || hasPreviewPermission || hasClonePermission; - }) - ); - } + return Combine(results); + } - return Combine(results); - } + public override async Task EditAsync(ContentItem contentItem, BuildEditorContext context) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - public override async Task EditAsync(ContentItem contentItem, BuildEditorContext context) + if (contentTypeDefinition == null) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - - if (contentTypeDefinition == null) - { - return null; - } + return null; + } - var user = _httpContextAccessor.HttpContext.User; + var user = _httpContextAccessor.HttpContext.User; - return Combine( - Dynamic("Content_PublishButton").Location("Actions:10") - .RenderWhen(() => _authorizationService.AuthorizeAsync(user, CommonPermissions.PublishContent, contentItem)), - Dynamic("Content_SaveDraftButton").Location("Actions:20") - .RenderWhen(async () => - { - return contentTypeDefinition.IsDraftable() - && await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, contentItem); - }) - ); - } + return Combine( + Dynamic("Content_PublishButton").Location("Actions:10") + .RenderWhen(() => _authorizationService.AuthorizeAsync(user, CommonPermissions.PublishContent, contentItem)), + Dynamic("Content_SaveDraftButton").Location("Actions:20") + .RenderWhen(async () => + { + return contentTypeDefinition.IsDraftable() + && await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, contentItem); + }) + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/DateEditorDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/DateEditorDriver.cs index 971ecb3595a..5735ea8ea49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/DateEditorDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/DateEditorDriver.cs @@ -7,54 +7,53 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Modules; -namespace OrchardCore.Contents.Drivers +namespace OrchardCore.Contents.Drivers; + +public sealed class DateEditorDriver : ContentPartDisplayDriver { - public sealed class DateEditorDriver : ContentPartDisplayDriver + private readonly ILocalClock _localClock; + + public DateEditorDriver(ILocalClock localClock) { - private readonly ILocalClock _localClock; + _localClock = localClock; + } - public DateEditorDriver(ILocalClock localClock) - { - _localClock = localClock; - } + public override IDisplayResult Edit(CommonPart part, BuildPartEditorContext context) + { + var settings = context.TypePartDefinition.GetSettings(); - public override IDisplayResult Edit(CommonPart part, BuildPartEditorContext context) + if (settings.DisplayDateEditor) { - var settings = context.TypePartDefinition.GetSettings(); - - if (settings.DisplayDateEditor) + return Initialize("CommonPart_Edit__Date", async model => { - return Initialize("CommonPart_Edit__Date", async model => - { - model.LocalDateTime = part.ContentItem.CreatedUtc.HasValue - ? (await _localClock.ConvertToLocalAsync(part.ContentItem.CreatedUtc.Value)).DateTime - : null; - }); - } - - return null; + model.LocalDateTime = part.ContentItem.CreatedUtc.HasValue + ? (await _localClock.ConvertToLocalAsync(part.ContentItem.CreatedUtc.Value)).DateTime + : null; + }); } - public override async Task UpdateAsync(CommonPart part, UpdatePartEditorContext context) + return null; + } + + public override async Task UpdateAsync(CommonPart part, UpdatePartEditorContext context) + { + var settings = context.TypePartDefinition.GetSettings(); + + if (settings.DisplayDateEditor) { - var settings = context.TypePartDefinition.GetSettings(); + var model = new DateEditorViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (settings.DisplayDateEditor) + if (model.LocalDateTime == null) { - var model = new DateEditorViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - if (model.LocalDateTime == null) - { - part.ContentItem.CreatedUtc = null; - } - else - { - part.ContentItem.CreatedUtc = await _localClock.ConvertToUtcAsync(model.LocalDateTime.Value); - } + part.ContentItem.CreatedUtc = null; + } + else + { + part.ContentItem.CreatedUtc = await _localClock.ConvertToUtcAsync(model.LocalDateTime.Value); } - - return Edit(part, context); } + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/OwnerEditorDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/OwnerEditorDriver.cs index e86418c8d7a..40eef5f54d4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/OwnerEditorDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Drivers/OwnerEditorDriver.cs @@ -11,92 +11,91 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Users; -namespace OrchardCore.Contents.Drivers +namespace OrchardCore.Contents.Drivers; + +public sealed class OwnerEditorDriver : ContentPartDisplayDriver { - public sealed class OwnerEditorDriver : ContentPartDisplayDriver + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly UserManager _userManager; + + internal readonly IStringLocalizer S; + + public OwnerEditorDriver(IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor, + UserManager userManager, + IStringLocalizer stringLocalizer) { - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly UserManager _userManager; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + _userManager = userManager; + S = stringLocalizer; + } - internal readonly IStringLocalizer S; + public override async Task EditAsync(CommonPart part, BuildPartEditorContext context) + { + var currentUser = _httpContextAccessor.HttpContext?.User; - public OwnerEditorDriver(IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor, - UserManager userManager, - IStringLocalizer stringLocalizer) + if (!await _authorizationService.AuthorizeAsync(currentUser, CommonPermissions.EditContentOwner, part.ContentItem)) { - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - _userManager = userManager; - S = stringLocalizer; + return null; } - public override async Task EditAsync(CommonPart part, BuildPartEditorContext context) - { - var currentUser = _httpContextAccessor.HttpContext?.User; + var settings = context.TypePartDefinition.GetSettings(); - if (!await _authorizationService.AuthorizeAsync(currentUser, CommonPermissions.EditContentOwner, part.ContentItem)) + if (settings.DisplayOwnerEditor) + { + return Initialize("CommonPart_Edit__Owner", async model => { - return null; - } + if (!string.IsNullOrEmpty(part.ContentItem.Owner)) + { + // TODO Move this editor to a user picker. + var user = await _userManager.FindByIdAsync(part.ContentItem.Owner); - var settings = context.TypePartDefinition.GetSettings(); + model.OwnerName = user?.UserName; + } + }); + } - if (settings.DisplayOwnerEditor) - { - return Initialize("CommonPart_Edit__Owner", async model => - { - if (!string.IsNullOrEmpty(part.ContentItem.Owner)) - { - // TODO Move this editor to a user picker. - var user = await _userManager.FindByIdAsync(part.ContentItem.Owner); - - model.OwnerName = user?.UserName; - } - }); - } + return null; + } + public override async Task UpdateAsync(CommonPart part, UpdatePartEditorContext context) + { + var currentUser = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(currentUser, CommonPermissions.EditContentOwner, part.ContentItem)) + { return null; } - public override async Task UpdateAsync(CommonPart part, UpdatePartEditorContext context) + var settings = context.TypePartDefinition.GetSettings(); + + if (settings.DisplayOwnerEditor) { - var currentUser = _httpContextAccessor.HttpContext?.User; + var model = new OwnerEditorViewModel(); - if (!await _authorizationService.AuthorizeAsync(currentUser, CommonPermissions.EditContentOwner, part.ContentItem)) + await context.Updater.TryUpdateModelAsync(model, Prefix); + + if (string.IsNullOrWhiteSpace(model.OwnerName)) { - return null; + context.Updater.ModelState.AddModelError(Prefix, nameof(model.OwnerName), S["A value is required for Owner."]); } - - var settings = context.TypePartDefinition.GetSettings(); - - if (settings.DisplayOwnerEditor) + else { - var model = new OwnerEditorViewModel(); + var newOwner = await _userManager.FindByNameAsync(model.OwnerName); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - if (string.IsNullOrWhiteSpace(model.OwnerName)) + if (newOwner == null) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.OwnerName), S["A value is required for Owner."]); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.OwnerName), S["Invalid username provided for Owner."]); } else { - var newOwner = await _userManager.FindByNameAsync(model.OwnerName); - - if (newOwner == null) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.OwnerName), S["Invalid username provided for Owner."]); - } - else - { - part.ContentItem.Owner = await _userManager.GetUserIdAsync(newOwner); - } + part.ContentItem.Owner = await _userManager.GetUserIdAsync(newOwner); } } - - return await EditAsync(part, context); } + + return await EditAsync(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Feeds/CommonFeedItemBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Feeds/CommonFeedItemBuilder.cs index 7c5be437324..8f0f30f305e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Feeds/CommonFeedItemBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Feeds/CommonFeedItemBuilder.cs @@ -7,78 +7,77 @@ using OrchardCore.Feeds; using OrchardCore.Feeds.Models; -namespace OrchardCore.Contents.Feeds.Builders +namespace OrchardCore.Contents.Feeds.Builders; + +public class CommonFeedItemBuilder : IFeedItemBuilder { - public class CommonFeedItemBuilder : IFeedItemBuilder + private readonly IContentManager _contentManager; + + public CommonFeedItemBuilder(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public CommonFeedItemBuilder(IContentManager contentManager) + public async Task PopulateAsync(FeedContext context) + { + foreach (var feedItem in context.Response.Items.OfType>()) { - _contentManager = contentManager; - } + var contentItem = feedItem.Item; + var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); + var bodyAspect = await _contentManager.PopulateAspectAsync(contentItem); + var routes = contentItemMetadata.DisplayRouteValues; - public async Task PopulateAsync(FeedContext context) - { - foreach (var feedItem in context.Response.Items.OfType>()) - { - var contentItem = feedItem.Item; - var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); - var bodyAspect = await _contentManager.PopulateAspectAsync(contentItem); - var routes = contentItemMetadata.DisplayRouteValues; + // author is intentionally left empty as it could result in unwanted spam - // author is intentionally left empty as it could result in unwanted spam + // add to known formats + if (context.Format == "rss") + { + var link = new XElement("link"); + var guid = new XElement("guid", new XAttribute("isPermaLink", "true")); - // add to known formats - if (context.Format == "rss") + context.Response.Contextualize(contextualize => { - var link = new XElement("link"); - var guid = new XElement("guid", new XAttribute("isPermaLink", "true")); + var request = contextualize.Url.ActionContext.HttpContext.Request; + var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); - context.Response.Contextualize(contextualize => - { - var request = contextualize.Url.ActionContext.HttpContext.Request; - var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); + link.Add(url); + guid.Add(url); + }); - link.Add(url); - guid.Add(url); - }); + feedItem.Element.SetElementValue("title", contentItem.DisplayText); + feedItem.Element.Add(link); - feedItem.Element.SetElementValue("title", contentItem.DisplayText); - feedItem.Element.Add(link); + feedItem.Element.Add(new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - feedItem.Element.Add(new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - - if (contentItem.PublishedUtc != null) - { - // RFC833 - // The "R" or "r" standard format specifier represents a custom date and time format string that is defined by - // the DateTimeFormatInfo.RFC1123Pattern property. The pattern reflects a defined standard, and the property - // is read-only. Therefore, it is always the same, regardless of the culture used or the format provider supplied. - // The custom format string is "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'". When this standard format specifier is used, - // the formatting or parsing operation always uses the invariant culture. - feedItem.Element.SetElementValue("pubDate", contentItem.PublishedUtc.Value.ToString("r")); - } - - feedItem.Element.Add(guid); + if (contentItem.PublishedUtc != null) + { + // RFC833 + // The "R" or "r" standard format specifier represents a custom date and time format string that is defined by + // the DateTimeFormatInfo.RFC1123Pattern property. The pattern reflects a defined standard, and the property + // is read-only. Therefore, it is always the same, regardless of the culture used or the format provider supplied. + // The custom format string is "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'". When this standard format specifier is used, + // the formatting or parsing operation always uses the invariant culture. + feedItem.Element.SetElementValue("pubDate", contentItem.PublishedUtc.Value.ToString("r")); } - else + + feedItem.Element.Add(guid); + } + else + { + context.Response.Contextualize(contextualize => { - context.Response.Contextualize(contextualize => - { - var request = contextualize.Url.ActionContext.HttpContext.Request; - var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); + var request = contextualize.Url.ActionContext.HttpContext.Request; + var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); - context.Builder.AddProperty(context, feedItem, "link", url); - }); + context.Builder.AddProperty(context, feedItem, "link", url); + }); - context.Builder.AddProperty(context, feedItem, "title", contentItem.DisplayText); - context.Builder.AddProperty(context, feedItem, new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); + context.Builder.AddProperty(context, feedItem, "title", contentItem.DisplayText); + context.Builder.AddProperty(context, feedItem, new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - if (contentItem.PublishedUtc != null) - { - context.Builder.AddProperty(context, feedItem, "published-date", contentItem.PublishedUtc.Value.ToString("r")); - } + if (contentItem.PublishedUtc != null) + { + context.Builder.AddProperty(context, feedItem, "published-date", contentItem.PublishedUtc.Value.ToString("r")); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/GraphQL/Startup.cs index 38d398b6355..f34c7b904b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/GraphQL/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement.GraphQL; using OrchardCore.Modules; -namespace OrchardCore.Contents.GraphQL +namespace OrchardCore.Contents.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddContentGraphQL(); - } + services.AddContentGraphQL(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/ContentsHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/ContentsHandler.cs index c51d591859c..a21bde7f827 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/ContentsHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/ContentsHandler.cs @@ -4,83 +4,82 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.Environment.Cache; -namespace OrchardCore.Contents.Handlers +namespace OrchardCore.Contents.Handlers; + +public class ContentsHandler : ContentHandlerBase { - public class ContentsHandler : ContentHandlerBase + private readonly ITagCache _tagCache; + + public ContentsHandler(ITagCache tagCache) { - private readonly ITagCache _tagCache; + _tagCache = tagCache; + } - public ContentsHandler(ITagCache tagCache) - { - _tagCache = tagCache; - } + public override Task PublishedAsync(PublishContentContext context) + { + return _tagCache.RemoveTagAsync($"contentitemid:{context.ContentItem.ContentItemId}"); + } - public override Task PublishedAsync(PublishContentContext context) + public override Task RemovedAsync(RemoveContentContext context) + { + if (context.NoActiveVersionLeft) { return _tagCache.RemoveTagAsync($"contentitemid:{context.ContentItem.ContentItemId}"); } - public override Task RemovedAsync(RemoveContentContext context) - { - if (context.NoActiveVersionLeft) - { - return _tagCache.RemoveTagAsync($"contentitemid:{context.ContentItem.ContentItemId}"); - } - - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public override Task UnpublishedAsync(PublishContentContext context) - { - return _tagCache.RemoveTagAsync($"contentitemid:{context.ContentItem.ContentItemId}"); - } + public override Task UnpublishedAsync(PublishContentContext context) + { + return _tagCache.RemoveTagAsync($"contentitemid:{context.ContentItem.ContentItemId}"); + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + { + return context.ForAsync(metadata => { - return context.ForAsync(metadata => + metadata.CreateRouteValues ??= new RouteValueDictionary { - metadata.CreateRouteValues ??= new RouteValueDictionary - { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Admin" }, - { "Action", "Create" }, - { "Id", context.ContentItem.ContentType }, - }; + { "Area", "OrchardCore.Contents" }, + { "Controller", "Admin" }, + { "Action", "Create" }, + { "Id", context.ContentItem.ContentType }, + }; - metadata.EditorRouteValues ??= new RouteValueDictionary - { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Admin" }, - { "Action", "Edit" }, - { "ContentItemId", context.ContentItem.ContentItemId }, - }; + metadata.EditorRouteValues ??= new RouteValueDictionary + { + { "Area", "OrchardCore.Contents" }, + { "Controller", "Admin" }, + { "Action", "Edit" }, + { "ContentItemId", context.ContentItem.ContentItemId }, + }; - metadata.AdminRouteValues ??= new RouteValueDictionary - { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Admin" }, - { "Action", "Edit" }, - { "ContentItemId", context.ContentItem.ContentItemId }, - }; + metadata.AdminRouteValues ??= new RouteValueDictionary + { + { "Area", "OrchardCore.Contents" }, + { "Controller", "Admin" }, + { "Action", "Edit" }, + { "ContentItemId", context.ContentItem.ContentItemId }, + }; - metadata.DisplayRouteValues ??= new RouteValueDictionary - { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Item" }, - { "Action", "Display" }, - { "ContentItemId", context.ContentItem.ContentItemId }, - }; + metadata.DisplayRouteValues ??= new RouteValueDictionary + { + { "Area", "OrchardCore.Contents" }, + { "Controller", "Item" }, + { "Action", "Display" }, + { "ContentItemId", context.ContentItem.ContentItemId }, + }; - metadata.RemoveRouteValues ??= new RouteValueDictionary - { - { "Area", "OrchardCore.Contents" }, - { "Controller", "Admin" }, - { "Action", "Remove" }, - { "ContentItemId", context.ContentItem.ContentItemId }, - }; + metadata.RemoveRouteValues ??= new RouteValueDictionary + { + { "Area", "OrchardCore.Contents" }, + { "Controller", "Admin" }, + { "Action", "Remove" }, + { "ContentItemId", context.ContentItem.ContentItemId }, + }; - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/FullTextAspectContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/FullTextAspectContentHandler.cs index c8e2a15e899..5de168c37d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/FullTextAspectContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Handlers/FullTextAspectContentHandler.cs @@ -13,70 +13,69 @@ using OrchardCore.Contents.Models; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Handlers +namespace OrchardCore.Contents.Handlers; + +/// +/// Provides the content for FullTextAspect based on FullTextAspectSettings. +/// +public class FullTextAspectContentHandler : ContentHandlerBase { - /// - /// Provides the content for FullTextAspect based on FullTextAspectSettings. - /// - public class FullTextAspectContentHandler : ContentHandlerBase + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IServiceProvider _serviceProvider; + + public FullTextAspectContentHandler( + IContentDefinitionManager contentDefinitionManager, + ILiquidTemplateManager liquidTemplateManager, + IServiceProvider serviceProvider + ) + { + _contentDefinitionManager = contentDefinitionManager; + _liquidTemplateManager = liquidTemplateManager; + _serviceProvider = serviceProvider; + } + + public override async Task GetContentItemAspectAsync(ContentItemAspectContext context) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IServiceProvider _serviceProvider; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - public FullTextAspectContentHandler( - IContentDefinitionManager contentDefinitionManager, - ILiquidTemplateManager liquidTemplateManager, - IServiceProvider serviceProvider - ) + if (contentTypeDefinition == null) { - _contentDefinitionManager = contentDefinitionManager; - _liquidTemplateManager = liquidTemplateManager; - _serviceProvider = serviceProvider; + return; } - public override async Task GetContentItemAspectAsync(ContentItemAspectContext context) + await context.ForAsync(async fullTextAspect => { var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); + var settings = contentTypeDefinition.GetSettings(); - if (contentTypeDefinition == null) + if (settings.IncludeDisplayText) { - return; + fullTextAspect.Segments.Add(context.ContentItem.DisplayText); } - await context.ForAsync(async fullTextAspect => + if (settings.IncludeBodyAspect) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - var settings = contentTypeDefinition.GetSettings(); + // Lazy resolution to prevent cyclic dependency of content handlers + var contentManager = _serviceProvider.GetRequiredService(); + var bodyAspect = await contentManager.PopulateAspectAsync(context.ContentItem); - if (settings.IncludeDisplayText) + if (bodyAspect != null && bodyAspect.Body != null) { - fullTextAspect.Segments.Add(context.ContentItem.DisplayText); - } - - if (settings.IncludeBodyAspect) - { - // Lazy resolution to prevent cyclic dependency of content handlers - var contentManager = _serviceProvider.GetRequiredService(); - var bodyAspect = await contentManager.PopulateAspectAsync(context.ContentItem); - - if (bodyAspect != null && bodyAspect.Body != null) - { - using var sw = new ZStringWriter(); - // Don't encode the body - bodyAspect.Body.WriteTo(sw, NullHtmlEncoder.Default); - fullTextAspect.Segments.Add(sw.ToString()); - } + using var sw = new ZStringWriter(); + // Don't encode the body + bodyAspect.Body.WriteTo(sw, NullHtmlEncoder.Default); + fullTextAspect.Segments.Add(sw.ToString()); } + } - if (settings.IncludeFullTextTemplate && !string.IsNullOrEmpty(settings.FullTextTemplate)) - { - var result = await _liquidTemplateManager.RenderStringAsync(settings.FullTextTemplate, NullEncoder.Default, context.ContentItem, - new Dictionary() { ["ContentItem"] = new ObjectValue(context.ContentItem) }); + if (settings.IncludeFullTextTemplate && !string.IsNullOrEmpty(settings.FullTextTemplate)) + { + var result = await _liquidTemplateManager.RenderStringAsync(settings.FullTextTemplate, NullEncoder.Default, context.ContentItem, + new Dictionary() { ["ContentItem"] = new ObjectValue(context.ContentItem) }); - fullTextAspect.Segments.Add(result); - } - }); - } + fullTextAspect.Segments.Add(result); + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/AspectsContentIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/AspectsContentIndexHandler.cs index 375ca1a40a1..7f162e64da1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/AspectsContentIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/AspectsContentIndexHandler.cs @@ -4,45 +4,44 @@ using OrchardCore.Indexing; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.Contents.Indexing +namespace OrchardCore.Contents.Indexing; + +public class AspectsContentIndexHandler : IContentItemIndexHandler { - public class AspectsContentIndexHandler : IContentItemIndexHandler + private readonly IContentManager _contentManager; + + public AspectsContentIndexHandler(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public AspectsContentIndexHandler(IContentManager contentManager) - { - _contentManager = contentManager; - } + public async Task BuildIndexAsync(BuildIndexContext context) + { + var body = await _contentManager.PopulateAspectAsync(context.ContentItem, new BodyAspect()); - public async Task BuildIndexAsync(BuildIndexContext context) + if (body != null && body.Body != null) { - var body = await _contentManager.PopulateAspectAsync(context.ContentItem, new BodyAspect()); - - if (body != null && body.Body != null) - { - context.DocumentIndex.Set( - IndexingConstants.BodyAspectBodyKey, - body.Body, - DocumentIndexOptions.Sanitize); - } - context.DocumentIndex.Set( - IndexingConstants.DisplayTextAnalyzedKey, - context.ContentItem.DisplayText, + IndexingConstants.BodyAspectBodyKey, + body.Body, DocumentIndexOptions.Sanitize); + } - // We need to store because of ContentPickerResultProvider(s) - context.DocumentIndex.Set( - IndexingConstants.DisplayTextKey + IndexingConstants.KeywordKey, - context.ContentItem.DisplayText, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + context.DocumentIndex.Set( + IndexingConstants.DisplayTextAnalyzedKey, + context.ContentItem.DisplayText, + DocumentIndexOptions.Sanitize); - // We need to store because of ContentPickerResultProvider(s) - context.DocumentIndex.Set( - IndexingConstants.DisplayTextNormalizedKey, - context.ContentItem.DisplayText?.ReplaceDiacritics().ToLower(), - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - } + // We need to store because of ContentPickerResultProvider(s) + context.DocumentIndex.Set( + IndexingConstants.DisplayTextKey + IndexingConstants.KeywordKey, + context.ContentItem.DisplayText, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + // We need to store because of ContentPickerResultProvider(s) + context.DocumentIndex.Set( + IndexingConstants.DisplayTextNormalizedKey, + context.ContentItem.DisplayText?.ReplaceDiacritics().ToLower(), + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/ContentItemIndexCoordinator.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/ContentItemIndexCoordinator.cs index 8ee8123ed7a..07292a5b289 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/ContentItemIndexCoordinator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/ContentItemIndexCoordinator.cs @@ -6,81 +6,80 @@ using OrchardCore.Indexing; using OrchardCore.Modules; -namespace OrchardCore.Contents.Indexing +namespace OrchardCore.Contents.Indexing; + +/// +/// Enumerates all parts and fields of content item to extract indexed properties. +/// +public class ContentItemIndexCoordinator : IContentItemIndexHandler { - /// - /// Enumerates all parts and fields of content item to extract indexed properties. - /// - public class ContentItemIndexCoordinator : IContentItemIndexHandler + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ITypeActivatorFactory _contentPartFactory; + private readonly IEnumerable _partIndexHandlers; + private readonly IEnumerable _fieldIndexHandlers; + private readonly ILogger _logger; + + public ContentItemIndexCoordinator( + IContentDefinitionManager contentDefinitionManager, + ITypeActivatorFactory contentPartFactory, + IEnumerable partIndexHandlers, + IEnumerable fieldIndexHandlers, + ILogger logger) + { + _contentDefinitionManager = contentDefinitionManager; + _contentPartFactory = contentPartFactory; + _partIndexHandlers = partIndexHandlers; + _fieldIndexHandlers = fieldIndexHandlers; + _logger = logger; + } + + public async Task BuildIndexAsync(BuildIndexContext context) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ITypeActivatorFactory _contentPartFactory; - private readonly IEnumerable _partIndexHandlers; - private readonly IEnumerable _fieldIndexHandlers; - private readonly ILogger _logger; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); - public ContentItemIndexCoordinator( - IContentDefinitionManager contentDefinitionManager, - ITypeActivatorFactory contentPartFactory, - IEnumerable partIndexHandlers, - IEnumerable fieldIndexHandlers, - ILogger logger) + if (contentTypeDefinition == null) { - _contentDefinitionManager = contentDefinitionManager; - _contentPartFactory = contentPartFactory; - _partIndexHandlers = partIndexHandlers; - _fieldIndexHandlers = fieldIndexHandlers; - _logger = logger; + return; } - public async Task BuildIndexAsync(BuildIndexContext context) + foreach (var contentTypePartDefinition in contentTypeDefinition.Parts) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(context.ContentItem.ContentType); + var partName = contentTypePartDefinition.Name; + var partTypeName = contentTypePartDefinition.PartDefinition.Name; + var partActivator = _contentPartFactory.GetTypeActivator(partTypeName); + var part = (ContentPart)context.ContentItem.Get(partActivator.Type, partName); + + var contentTypePartDefinitionMethod = contentTypePartDefinition.GetType().GetMethod("GetSettings"); + var contentTypePartDefinitionGeneric = contentTypePartDefinitionMethod.MakeGenericMethod(context.Settings.GetType()); + var typePartIndexSettings = (IContentIndexSettings)contentTypePartDefinitionGeneric.Invoke(contentTypePartDefinition, null); - if (contentTypeDefinition == null) + // Skip this part if it's not included in the index and it's not the default type part + if (contentTypeDefinition.Name != partTypeName && !typePartIndexSettings.Included) { - return; + continue; } - foreach (var contentTypePartDefinition in contentTypeDefinition.Parts) - { - var partName = contentTypePartDefinition.Name; - var partTypeName = contentTypePartDefinition.PartDefinition.Name; - var partActivator = _contentPartFactory.GetTypeActivator(partTypeName); - var part = (ContentPart)context.ContentItem.Get(partActivator.Type, partName); + await _partIndexHandlers.InvokeAsync((handler, part, contentTypePartDefinition, context, typePartIndexSettings) => + handler.BuildIndexAsync(part, contentTypePartDefinition, context, typePartIndexSettings), + part, contentTypePartDefinition, context, typePartIndexSettings, _logger); - var contentTypePartDefinitionMethod = contentTypePartDefinition.GetType().GetMethod("GetSettings"); - var contentTypePartDefinitionGeneric = contentTypePartDefinitionMethod.MakeGenericMethod(context.Settings.GetType()); - var typePartIndexSettings = (IContentIndexSettings)contentTypePartDefinitionGeneric.Invoke(contentTypePartDefinition, null); + foreach (var contentPartFieldDefinition in contentTypePartDefinition.PartDefinition.Fields) + { + var contentPartFieldDefinitionMethod = contentPartFieldDefinition.GetType().GetMethod("GetSettings"); + var contentPartFieldDefinitionGeneric = contentPartFieldDefinitionMethod.MakeGenericMethod(context.Settings.GetType()); + var partFieldIndexSettings = (IContentIndexSettings)contentPartFieldDefinitionGeneric.Invoke(contentPartFieldDefinition, null); - // Skip this part if it's not included in the index and it's not the default type part - if (contentTypeDefinition.Name != partTypeName && !typePartIndexSettings.Included) + if (!partFieldIndexSettings.Included) { continue; } - await _partIndexHandlers.InvokeAsync((handler, part, contentTypePartDefinition, context, typePartIndexSettings) => - handler.BuildIndexAsync(part, contentTypePartDefinition, context, typePartIndexSettings), - part, contentTypePartDefinition, context, typePartIndexSettings, _logger); - - foreach (var contentPartFieldDefinition in contentTypePartDefinition.PartDefinition.Fields) - { - var contentPartFieldDefinitionMethod = contentPartFieldDefinition.GetType().GetMethod("GetSettings"); - var contentPartFieldDefinitionGeneric = contentPartFieldDefinitionMethod.MakeGenericMethod(context.Settings.GetType()); - var partFieldIndexSettings = (IContentIndexSettings)contentPartFieldDefinitionGeneric.Invoke(contentPartFieldDefinition, null); - - if (!partFieldIndexSettings.Included) - { - continue; - } - - await _fieldIndexHandlers.InvokeAsync((handler, part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings) => - handler.BuildIndexAsync(part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings), - part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings, _logger); - } + await _fieldIndexHandlers.InvokeAsync((handler, part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings) => + handler.BuildIndexAsync(part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings), + part, contentTypePartDefinition, contentPartFieldDefinition, context, partFieldIndexSettings, _logger); } - - return; } + + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/DefaultContentIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/DefaultContentIndexHandler.cs index 4c987038256..16026c6820b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/DefaultContentIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/DefaultContentIndexHandler.cs @@ -1,54 +1,53 @@ using System.Threading.Tasks; using OrchardCore.Indexing; -namespace OrchardCore.Contents.Indexing +namespace OrchardCore.Contents.Indexing; + +public class DefaultContentIndexHandler : IContentItemIndexHandler { - public class DefaultContentIndexHandler : IContentItemIndexHandler + public Task BuildIndexAsync(BuildIndexContext context) { - public Task BuildIndexAsync(BuildIndexContext context) - { - context.DocumentIndex.Set( - IndexingConstants.ContentTypeKey, - context.ContentItem.ContentType, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.CreatedUtcKey, - context.ContentItem.CreatedUtc, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.LatestKey, - context.ContentItem.Latest, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.OwnerKey, - context.ContentItem.Owner, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.AuthorKey, - context.ContentItem.Author, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.ModifiedUtcKey, - context.ContentItem.ModifiedUtc, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - // We need to store because of ContentPickerResultProvider(s) - context.DocumentIndex.Set( - IndexingConstants.PublishedKey, - context.ContentItem.Published, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - context.DocumentIndex.Set( - IndexingConstants.PublishedUtcKey, - context.ContentItem.PublishedUtc, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - - return Task.CompletedTask; - } + context.DocumentIndex.Set( + IndexingConstants.ContentTypeKey, + context.ContentItem.ContentType, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.CreatedUtcKey, + context.ContentItem.CreatedUtc, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.LatestKey, + context.ContentItem.Latest, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.OwnerKey, + context.ContentItem.Owner, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.AuthorKey, + context.ContentItem.Author, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.ModifiedUtcKey, + context.ContentItem.ModifiedUtc, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + // We need to store because of ContentPickerResultProvider(s) + context.DocumentIndex.Set( + IndexingConstants.PublishedKey, + context.ContentItem.Published, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + context.DocumentIndex.Set( + IndexingConstants.PublishedUtcKey, + context.ContentItem.PublishedUtc, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/FullTextContentIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/FullTextContentIndexHandler.cs index 6030782a988..27047df3b64 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/FullTextContentIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Indexing/FullTextContentIndexHandler.cs @@ -4,32 +4,31 @@ using OrchardCore.ContentManagement.Models; using OrchardCore.Indexing; -namespace OrchardCore.Contents.Indexing -{ - public class FullTextContentIndexHandler(IContentManager contentManager) : IContentItemIndexHandler - { - private readonly IContentManager _contentManager = contentManager; +namespace OrchardCore.Contents.Indexing; - public async Task BuildIndexAsync(BuildIndexContext context) - { - var result = await _contentManager.PopulateAspectAsync(context.ContentItem); +public class FullTextContentIndexHandler(IContentManager contentManager) : IContentItemIndexHandler +{ + private readonly IContentManager _contentManager = contentManager; - using var stringBuilder = ZString.CreateStringBuilder(); + public async Task BuildIndexAsync(BuildIndexContext context) + { + var result = await _contentManager.PopulateAspectAsync(context.ContentItem); - foreach (var segment in result.Segments) - { - stringBuilder.Append(segment); - stringBuilder.Append(" "); - } + using var stringBuilder = ZString.CreateStringBuilder(); - var value = stringBuilder.ToString(); + foreach (var segment in result.Segments) + { + stringBuilder.Append(segment); + stringBuilder.Append(" "); + } - if (string.IsNullOrEmpty(value)) - { - return; - } + var value = stringBuilder.ToString(); - context.DocumentIndex.Set(IndexingConstants.FullTextKey, value, DocumentIndexOptions.Sanitize); + if (string.IsNullOrEmpty(value)) + { + return; } + + context.DocumentIndex.Set(IndexingConstants.FullTextKey, value, DocumentIndexOptions.Sanitize); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/BuildDisplayFilter.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/BuildDisplayFilter.cs index 8112c3aa4b4..29c1f8d13ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/BuildDisplayFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/BuildDisplayFilter.cs @@ -9,70 +9,69 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class BuildDisplayFilter : ILiquidFilter { - public class BuildDisplayFilter : ILiquidFilter - { - private const int DefaultMaxContentItemRecursions = 20; + private const int DefaultMaxContentItemRecursions = 20; - private readonly IContentItemRecursionHelper _buildDisplayRecursionHelper; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IContentItemRecursionHelper _buildDisplayRecursionHelper; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IUpdateModelAccessor _updateModelAccessor; - public BuildDisplayFilter( - IContentItemRecursionHelper buildDisplayRecursionHelper, - IContentItemDisplayManager contentItemDisplayManager, - IUpdateModelAccessor updateModelAccessor) - { - _buildDisplayRecursionHelper = buildDisplayRecursionHelper; - _contentItemDisplayManager = contentItemDisplayManager; - _updateModelAccessor = updateModelAccessor; - } + public BuildDisplayFilter( + IContentItemRecursionHelper buildDisplayRecursionHelper, + IContentItemDisplayManager contentItemDisplayManager, + IUpdateModelAccessor updateModelAccessor) + { + _buildDisplayRecursionHelper = buildDisplayRecursionHelper; + _contentItemDisplayManager = contentItemDisplayManager; + _updateModelAccessor = updateModelAccessor; + } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + static async ValueTask Awaited(Task task, TemplateOptions options) { - static async ValueTask Awaited(Task task, TemplateOptions options) - { - return FluidValue.Create(await task, options); - } - - var obj = input.ToObjectValue(); + return FluidValue.Create(await task, options); + } - if (obj is not ContentItem contentItem) - { - contentItem = null; + var obj = input.ToObjectValue(); - if (obj is JsonObject jObject) - { - contentItem = jObject.ToObject(); - } - } + if (obj is not ContentItem contentItem) + { + contentItem = null; - // If input is a 'JObject' but which not represents a 'ContentItem', - // a 'ContentItem' is still created but with some null properties. - if (contentItem?.ContentItemId == null) + if (obj is JsonObject jObject) { - return new ValueTask(NilValue.Instance); + contentItem = jObject.ToObject(); } + } - // When {{ Model.ContentItem | shape_build_display | shape_render }} is called prevent unlimited recursions. - // max_recursions is an optional argument to override the default limit of 20. - var maxRecursions = arguments["max_recursions"]; - var recursionLimit = maxRecursions.Type == FluidValues.Number ? Convert.ToInt32(maxRecursions.ToNumberValue()) : DefaultMaxContentItemRecursions; - if (_buildDisplayRecursionHelper.IsRecursive(contentItem, recursionLimit)) - { - return new ValueTask(NilValue.Instance); - } + // If input is a 'JObject' but which not represents a 'ContentItem', + // a 'ContentItem' is still created but with some null properties. + if (contentItem?.ContentItemId == null) + { + return new ValueTask(NilValue.Instance); + } - var displayType = arguments["type"].Or(arguments.At(0)).ToStringValue(); + // When {{ Model.ContentItem | shape_build_display | shape_render }} is called prevent unlimited recursions. + // max_recursions is an optional argument to override the default limit of 20. + var maxRecursions = arguments["max_recursions"]; + var recursionLimit = maxRecursions.Type == FluidValues.Number ? Convert.ToInt32(maxRecursions.ToNumberValue()) : DefaultMaxContentItemRecursions; + if (_buildDisplayRecursionHelper.IsRecursive(contentItem, recursionLimit)) + { + return new ValueTask(NilValue.Instance); + } - var task = _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, displayType); - if (task.IsCompletedSuccessfully) - { - return new ValueTask(FluidValue.Create(task.Result, ctx.Options)); - } + var displayType = arguments["type"].Or(arguments.At(0)).ToStringValue(); - return Awaited(task, ctx.Options); + var task = _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, displayType); + if (task.IsCompletedSuccessfully) + { + return new ValueTask(FluidValue.Create(task.Result, ctx.Options)); } + + return Awaited(task, ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentAnchorTag.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentAnchorTag.cs index d15dc5bfbd8..cd6079abdff 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentAnchorTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentAnchorTag.cs @@ -14,228 +14,227 @@ using OrchardCore.DisplayManagement.Liquid.Tags; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class ContentAnchorTag : IAnchorTag { - public class ContentAnchorTag : IAnchorTag - { - public int Order => -10; + public int Order => -10; - public bool Match(IReadOnlyList argumentsList) + public bool Match(IReadOnlyList argumentsList) + { + foreach (var argument in argumentsList) { - foreach (var argument in argumentsList) + switch (argument.Name) { - switch (argument.Name) - { - case "admin_for": - case "display_for": - case "edit_for": - case "remove_for": - case "create_for": return true; - } + case "admin_for": + case "display_for": + case "edit_for": + case "remove_for": + case "create_for": return true; } - - return false; } - public async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, LiquidTemplateContext context) - { - var services = context.Services; - var viewContext = context.ViewContext; - var urlHelperFactory = services.GetRequiredService(); - var contentManager = services.GetRequiredService(); + return false; + } + + public async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, LiquidTemplateContext context) + { + var services = context.Services; + var viewContext = context.ViewContext; + var urlHelperFactory = services.GetRequiredService(); + var contentManager = services.GetRequiredService(); - ContentItem adminFor = null; - ContentItem displayFor = null; - ContentItem editFor = null; - ContentItem removeFor = null; - ContentItem createFor = null; + ContentItem adminFor = null; + ContentItem displayFor = null; + ContentItem editFor = null; + ContentItem removeFor = null; + ContentItem createFor = null; - Dictionary routeValues = null; - Dictionary customAttributes = []; + Dictionary routeValues = null; + Dictionary customAttributes = []; - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "admin_for": adminFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; - case "display_for": displayFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; - case "edit_for": editFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; - case "remove_for": removeFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; - case "create_for": createFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; + case "admin_for": adminFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; + case "display_for": displayFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; + case "edit_for": editFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; + case "remove_for": removeFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; + case "create_for": createFor = (await argument.Expression.EvaluateAsync(context)).ToObjectValue() as ContentItem; break; - default: + default: - if (argument.Name.StartsWith("route_", StringComparison.OrdinalIgnoreCase)) - { - routeValues ??= []; - routeValues[argument.Name[6..]] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); - } - else - { - customAttributes[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); - } + if (argument.Name.StartsWith("route_", StringComparison.OrdinalIgnoreCase)) + { + routeValues ??= []; + routeValues[argument.Name[6..]] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); + } + else + { + customAttributes[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); + } - break; - } + break; } + } - ContentItem contentItem = null; + ContentItem contentItem = null; - var urlHelper = urlHelperFactory.GetUrlHelper(viewContext); + var urlHelper = urlHelperFactory.GetUrlHelper(viewContext); - if (displayFor != null) - { - contentItem = displayFor; - var previewAspect = await contentManager.PopulateAspectAsync(contentItem); + if (displayFor != null) + { + contentItem = displayFor; + var previewAspect = await contentManager.PopulateAspectAsync(contentItem); - if (!string.IsNullOrEmpty(previewAspect.PreviewUrl)) + if (!string.IsNullOrEmpty(previewAspect.PreviewUrl)) + { + var previewUrl = previewAspect.PreviewUrl; + if (!previewUrl.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) { - var previewUrl = previewAspect.PreviewUrl; - if (!previewUrl.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) + if (previewUrl.StartsWith('/')) { - if (previewUrl.StartsWith('/')) - { - previewUrl = '~' + previewUrl; - } - else - { - previewUrl = "~/" + previewUrl; - } + previewUrl = '~' + previewUrl; } - - customAttributes["href"] = urlHelper.Content(previewUrl); - } - else - { - var metadata = await contentManager.PopulateAspectAsync(displayFor); - - if (metadata.DisplayRouteValues != null) + else { - if (routeValues != null) - { - foreach (var attribute in routeValues) - { - metadata.DisplayRouteValues.Add(attribute.Key, attribute.Value); - } - } - - customAttributes["href"] = urlHelper.Action(metadata.DisplayRouteValues["action"].ToString(), metadata.DisplayRouteValues); + previewUrl = "~/" + previewUrl; } } + + customAttributes["href"] = urlHelper.Content(previewUrl); } - else if (editFor != null) + else { - var metadata = await PopulateAspectForContentItemMetadataAsync(editFor); + var metadata = await contentManager.PopulateAspectAsync(displayFor); - if (metadata.EditorRouteValues != null) + if (metadata.DisplayRouteValues != null) { if (routeValues != null) { foreach (var attribute in routeValues) { - metadata.EditorRouteValues.Add(attribute.Key, attribute.Value); + metadata.DisplayRouteValues.Add(attribute.Key, attribute.Value); } } - customAttributes["href"] = urlHelper.Action(metadata.EditorRouteValues["action"].ToString(), metadata.EditorRouteValues); + customAttributes["href"] = urlHelper.Action(metadata.DisplayRouteValues["action"].ToString(), metadata.DisplayRouteValues); } } - else if (adminFor != null) - { - var metadata = await PopulateAspectForContentItemMetadataAsync(adminFor); + } + else if (editFor != null) + { + var metadata = await PopulateAspectForContentItemMetadataAsync(editFor); - if (metadata.AdminRouteValues != null) + if (metadata.EditorRouteValues != null) + { + if (routeValues != null) { - if (routeValues != null) + foreach (var attribute in routeValues) { - foreach (var attribute in routeValues) - { - metadata.AdminRouteValues.Add(attribute.Key, attribute.Value); - } + metadata.EditorRouteValues.Add(attribute.Key, attribute.Value); } - - customAttributes["href"] = urlHelper.Action(metadata.AdminRouteValues["action"].ToString(), metadata.AdminRouteValues); } + + customAttributes["href"] = urlHelper.Action(metadata.EditorRouteValues["action"].ToString(), metadata.EditorRouteValues); } - else if (removeFor != null) - { - var metadata = await PopulateAspectForContentItemMetadataAsync(removeFor); + } + else if (adminFor != null) + { + var metadata = await PopulateAspectForContentItemMetadataAsync(adminFor); - if (metadata.RemoveRouteValues != null) + if (metadata.AdminRouteValues != null) + { + if (routeValues != null) { - if (routeValues != null) + foreach (var attribute in routeValues) { - foreach (var attribute in routeValues) - { - metadata.RemoveRouteValues.Add(attribute.Key, attribute.Value); - } + metadata.AdminRouteValues.Add(attribute.Key, attribute.Value); } - - customAttributes["href"] = urlHelper.Action(metadata.RemoveRouteValues["action"].ToString(), metadata.RemoveRouteValues); } + + customAttributes["href"] = urlHelper.Action(metadata.AdminRouteValues["action"].ToString(), metadata.AdminRouteValues); } - else if (createFor != null) - { - var metadata = await PopulateAspectForContentItemMetadataAsync(createFor); + } + else if (removeFor != null) + { + var metadata = await PopulateAspectForContentItemMetadataAsync(removeFor); - if (metadata.CreateRouteValues != null) + if (metadata.RemoveRouteValues != null) + { + if (routeValues != null) { - if (routeValues != null) + foreach (var attribute in routeValues) { - foreach (var attribute in routeValues) - { - metadata.CreateRouteValues.Add(attribute.Key, attribute.Value); - } + metadata.RemoveRouteValues.Add(attribute.Key, attribute.Value); } - - customAttributes["href"] = urlHelper.Action(metadata.CreateRouteValues["action"].ToString(), metadata.CreateRouteValues); } + + customAttributes["href"] = urlHelper.Action(metadata.RemoveRouteValues["action"].ToString(), metadata.RemoveRouteValues); } + } + else if (createFor != null) + { + var metadata = await PopulateAspectForContentItemMetadataAsync(createFor); - if (customAttributes.Count == 0) + if (metadata.CreateRouteValues != null) { - return Completion.Normal; + if (routeValues != null) + { + foreach (var attribute in routeValues) + { + metadata.CreateRouteValues.Add(attribute.Key, attribute.Value); + } + } + + customAttributes["href"] = urlHelper.Action(metadata.CreateRouteValues["action"].ToString(), metadata.CreateRouteValues); } + } - var tagBuilder = new TagBuilder("a"); + if (customAttributes.Count == 0) + { + return Completion.Normal; + } - foreach (var attribute in customAttributes) - { - tagBuilder.Attributes[attribute.Key] = attribute.Value; - } + var tagBuilder = new TagBuilder("a"); - tagBuilder.RenderStartTag().WriteTo(writer, (HtmlEncoder)encoder); + foreach (var attribute in customAttributes) + { + tagBuilder.Attributes[attribute.Key] = attribute.Value; + } - if (statements != null && statements.Count > 0) - { - var completion = await statements.RenderStatementsAsync(writer, encoder, context); + tagBuilder.RenderStartTag().WriteTo(writer, (HtmlEncoder)encoder); - if (completion != Completion.Normal) - { - return completion; - } - } - else if (!string.IsNullOrEmpty(contentItem.DisplayText)) - { - writer.Write(encoder.Encode(contentItem.DisplayText)); - } - else + if (statements != null && statements.Count > 0) + { + var completion = await statements.RenderStatementsAsync(writer, encoder, context); + + if (completion != Completion.Normal) { - var contentDefinitionManager = services.GetRequiredService(); - var typeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - writer.Write(encoder.Encode(typeDefinition.ToString())); + return completion; } + } + else if (!string.IsNullOrEmpty(contentItem.DisplayText)) + { + writer.Write(encoder.Encode(contentItem.DisplayText)); + } + else + { + var contentDefinitionManager = services.GetRequiredService(); + var typeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + writer.Write(encoder.Encode(typeDefinition.ToString())); + } - tagBuilder.RenderEndTag().WriteTo(writer, (HtmlEncoder)encoder); + tagBuilder.RenderEndTag().WriteTo(writer, (HtmlEncoder)encoder); - return Completion.Normal; + return Completion.Normal; - async Task PopulateAspectForContentItemMetadataAsync(ContentItem item) - { - contentItem = item; + async Task PopulateAspectForContentItemMetadataAsync(ContentItem item) + { + contentItem = item; - return await contentManager.PopulateAspectAsync(item); - } + return await contentManager.PopulateAspectAsync(item); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemFilter.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemFilter.cs index b9deac61e8e..820b934ed3c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemFilter.cs @@ -5,30 +5,29 @@ using OrchardCore.ContentManagement; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class ContentItemFilter : ILiquidFilter { - public class ContentItemFilter : ILiquidFilter - { - private readonly IContentManager _contentManager; + private readonly IContentManager _contentManager; - public ContentItemFilter(IContentManager contentManager) - { - _contentManager = contentManager; - } + public ContentItemFilter(IContentManager contentManager) + { + _contentManager = contentManager; + } - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + if (input.Type == FluidValues.Array) { - if (input.Type == FluidValues.Array) - { - // List of content item ids to return. - var contentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue()); + // List of content item ids to return. + var contentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue()); - return FluidValue.Create(await _contentManager.GetAsync(contentItemIds), ctx.Options); - } + return FluidValue.Create(await _contentManager.GetAsync(contentItemIds), ctx.Options); + } - var contentItemId = input.ToStringValue(); + var contentItemId = input.ToStringValue(); - return FluidValue.Create(await _contentManager.GetAsync(contentItemId), ctx.Options); - } + return FluidValue.Create(await _contentManager.GetAsync(contentItemId), ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemRecursionHelper.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemRecursionHelper.cs index c955912bfc5..307c6dc6832 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemRecursionHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemRecursionHelper.cs @@ -1,42 +1,41 @@ using System.Collections.Generic; using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public interface IContentItemRecursionHelper { - public interface IContentItemRecursionHelper - { - /// - /// Returns when the has already been evaluated during this request by the particular filter./>. - /// - bool IsRecursive(ContentItem contentItem, int maxRecursions = 1); - } + /// + /// Returns when the has already been evaluated during this request by the particular filter./>. + /// + bool IsRecursive(ContentItem contentItem, int maxRecursions = 1); +} + +/// +public class ContentItemRecursionHelper : IContentItemRecursionHelper +{ + private readonly Dictionary _recursions = []; /// - public class ContentItemRecursionHelper : IContentItemRecursionHelper + public bool IsRecursive(ContentItem contentItem, int maxRecursions = 1) { - private readonly Dictionary _recursions = []; - - /// - public bool IsRecursive(ContentItem contentItem, int maxRecursions = 1) + if (_recursions.TryGetValue(contentItem, out var counter)) { - if (_recursions.TryGetValue(contentItem, out var counter)) + if (maxRecursions < 1) { - if (maxRecursions < 1) - { - maxRecursions = 1; - } - - if (counter > maxRecursions) - { - return true; - } + maxRecursions = 1; + } - _recursions[contentItem] = counter + 1; - return false; + if (counter > maxRecursions) + { + return true; } - _recursions[contentItem] = 1; + _recursions[contentItem] = counter + 1; return false; } + + _recursions[contentItem] = 1; + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemTag.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemTag.cs index 5d6c92bbd92..9fee5274105 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/ContentItemTag.cs @@ -7,20 +7,19 @@ using Fluid.Values; using OrchardCore.DisplayManagement.Liquid.Tags; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class ContentItemTag { - public class ContentItemTag - { - private static readonly FilterArgument _typeArgument = new("type", new LiteralExpression(StringValue.Create("contentitem"))); + private static readonly FilterArgument _typeArgument = new("type", new LiteralExpression(StringValue.Create("contentitem"))); - public static ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder encoder, TemplateContext context) + public static ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder encoder, TemplateContext context) + { + var list = new List(argumentsList) { - var list = new List(argumentsList) - { - _typeArgument - }; + _typeArgument + }; - return ShapeTag.WriteToAsync(list, writer, encoder, context); - } + return ShapeTag.WriteToAsync(list, writer, encoder, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayTextFilter.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayTextFilter.cs index bcae2c11433..ccd79606b8c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayTextFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayTextFilter.cs @@ -3,20 +3,19 @@ using Fluid.Values; using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public static class DisplayTextFilter { - public static class DisplayTextFilter + public static ValueTask DisplayText(FluidValue input, FilterArguments _1, TemplateContext _2) { - public static ValueTask DisplayText(FluidValue input, FilterArguments _1, TemplateContext _2) - { - var contentItem = input.ToObjectValue() as ContentItem; - - if (contentItem == null) - { - return new ValueTask(NilValue.Instance); - } + var contentItem = input.ToObjectValue() as ContentItem; - return new ValueTask(new StringValue(contentItem.DisplayText ?? "")); + if (contentItem == null) + { + return new ValueTask(NilValue.Instance); } + + return new ValueTask(new StringValue(contentItem.DisplayText ?? "")); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayUrlFilter.cs index 12cc5a14ab5..40ebb68122b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/DisplayUrlFilter.cs @@ -9,49 +9,48 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class DisplayUrlFilter : ILiquidFilter { - public class DisplayUrlFilter : ILiquidFilter + private readonly AutorouteOptions _autorouteOptions; + private readonly IContentManager _contentManager; + private readonly IUrlHelperFactory _urlHelperFactory; + + public DisplayUrlFilter(IOptions autorouteOptions, IContentManager contentManager, IUrlHelperFactory urlHelperFactory) { - private readonly AutorouteOptions _autorouteOptions; - private readonly IContentManager _contentManager; - private readonly IUrlHelperFactory _urlHelperFactory; + _autorouteOptions = autorouteOptions.Value; + _contentManager = contentManager; + _urlHelperFactory = urlHelperFactory; + } - public DisplayUrlFilter(IOptions autorouteOptions, IContentManager contentManager, IUrlHelperFactory urlHelperFactory) - { - _autorouteOptions = autorouteOptions.Value; - _contentManager = contentManager; - _urlHelperFactory = urlHelperFactory; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + { + var contentItem = input.ToObjectValue() as ContentItem; + RouteValueDictionary routeValues; - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + if (contentItem == null) { - var contentItem = input.ToObjectValue() as ContentItem; - RouteValueDictionary routeValues; - - if (contentItem == null) + if (string.IsNullOrEmpty(input.ToStringValue())) { - if (string.IsNullOrEmpty(input.ToStringValue())) - { - return StringValue.Empty; - } - - routeValues = new RouteValueDictionary(_autorouteOptions.GlobalRouteValues) - { - [_autorouteOptions.ContentItemIdKey] = input.ToStringValue(), - }; + return StringValue.Empty; } - else + + routeValues = new RouteValueDictionary(_autorouteOptions.GlobalRouteValues) { - var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); - routeValues = contentItemMetadata.DisplayRouteValues; - } + [_autorouteOptions.ContentItemIdKey] = input.ToStringValue(), + }; + } + else + { + var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); + routeValues = contentItemMetadata.DisplayRouteValues; + } - var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); + var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); - var linkUrl = urlHelper.RouteUrl(routeValues); + var linkUrl = urlHelper.RouteUrl(routeValues); - return new StringValue(linkUrl); - } + return new StringValue(linkUrl); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/FullTextFilter.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/FullTextFilter.cs index 7af91c09a06..5df638cb449 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/FullTextFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Liquid/FullTextFilter.cs @@ -8,90 +8,89 @@ using OrchardCore.ContentManagement.Models; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Liquid +namespace OrchardCore.Contents.Liquid; + +public class FullTextFilter : ILiquidFilter { - public class FullTextFilter : ILiquidFilter - { - private readonly IContentManager _contentManager; - private readonly IContentItemRecursionHelper _fullTextRecursionHelper; + private readonly IContentManager _contentManager; + private readonly IContentItemRecursionHelper _fullTextRecursionHelper; - public FullTextFilter( - IContentManager contentManager, - IContentItemRecursionHelper fullTextRecursionHelper) - { - _contentManager = contentManager; - _fullTextRecursionHelper = fullTextRecursionHelper; - } + public FullTextFilter( + IContentManager contentManager, + IContentItemRecursionHelper fullTextRecursionHelper) + { + _contentManager = contentManager; + _fullTextRecursionHelper = fullTextRecursionHelper; + } - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + if (input.Type == FluidValues.Array) { - if (input.Type == FluidValues.Array) + var contentItems = new List(); + foreach (var objValue in input.Enumerate(ctx)) { - var contentItems = new List(); - foreach (var objValue in input.Enumerate(ctx)) + var contentItem = GetContentItem(objValue); + if (contentItem != null) { - var contentItem = GetContentItem(objValue); - if (contentItem != null) + if (!_fullTextRecursionHelper.IsRecursive(contentItem)) { - if (!_fullTextRecursionHelper.IsRecursive(contentItem)) - { - contentItems.Add(contentItem); - } + contentItems.Add(contentItem); } } + } - if (contentItems.Count == 0) - { - return NilValue.Instance; - } - - var aspects = new List(); + if (contentItems.Count == 0) + { + return NilValue.Instance; + } - foreach (var contentItem in contentItems) - { - aspects.Add(await _contentManager.PopulateAspectAsync(contentItem)); - } + var aspects = new List(); - // When returning segments separate them so multiple segments are indexed individually. - return new ArrayValue(aspects.SelectMany(x => x.Segments).Where(x => !string.IsNullOrEmpty(x)).Select(x => new StringValue(x + ' ')).ToArray()); - } - else + foreach (var contentItem in contentItems) { - var contentItem = GetContentItem(input); - - if (contentItem == null || _fullTextRecursionHelper.IsRecursive(contentItem)) - { - return NilValue.Instance; - } + aspects.Add(await _contentManager.PopulateAspectAsync(contentItem)); + } - var fullTextAspect = await _contentManager.PopulateAspectAsync(contentItem); + // When returning segments separate them so multiple segments are indexed individually. + return new ArrayValue(aspects.SelectMany(x => x.Segments).Where(x => !string.IsNullOrEmpty(x)).Select(x => new StringValue(x + ' ')).ToArray()); + } + else + { + var contentItem = GetContentItem(input); - // Remove empty strings as display text is often unused in contained content items. - return new ArrayValue(fullTextAspect.Segments.Where(x => !string.IsNullOrEmpty(x)).Select(x => new StringValue(x)).ToArray()); + if (contentItem == null || _fullTextRecursionHelper.IsRecursive(contentItem)) + { + return NilValue.Instance; } + + var fullTextAspect = await _contentManager.PopulateAspectAsync(contentItem); + + // Remove empty strings as display text is often unused in contained content items. + return new ArrayValue(fullTextAspect.Segments.Where(x => !string.IsNullOrEmpty(x)).Select(x => new StringValue(x)).ToArray()); } + } + + private static ContentItem GetContentItem(FluidValue input) + { + var obj = input.ToObjectValue(); - private static ContentItem GetContentItem(FluidValue input) + if (obj is not ContentItem contentItem) { - var obj = input.ToObjectValue(); + contentItem = null; - if (obj is not ContentItem contentItem) + if (obj is JsonObject jObject) { - contentItem = null; - - if (obj is JsonObject jObject) + contentItem = jObject.ToObject(); + // If input is a 'JObject' but which not represents a 'ContentItem', + // a 'ContentItem' is still created but with some null properties. + if (contentItem?.ContentItemId == null) { - contentItem = jObject.ToObject(); - // If input is a 'JObject' but which not represents a 'ContentItem', - // a 'ContentItem' is still created but with some null properties. - if (contentItem?.ContentItemId == null) - { - return null; - } + return null; } } - - return contentItem; } + + return contentItem; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Migrations.cs index 7610eaf1c07..483b613c961 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Migrations.cs @@ -3,24 +3,23 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.Contents +namespace OrchardCore.Contents; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("CommonPart", builder => builder - .Attachable() - .WithDescription("Provides an editor for the common properties of a content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("CommonPart", builder => builder + .Attachable() + .WithDescription("Provides an editor for the common properties of a content item.")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPart.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPart.cs index 511a6669d2b..e5c5e116d7f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPart.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.Models +namespace OrchardCore.Contents.Models; + +/// +/// When attached to a content type, provides a way to edit the common +/// properties of a content item like CreatedUtc and Owner. +/// +public class CommonPart : ContentPart { - /// - /// When attached to a content type, provides a way to edit the common - /// properties of a content item like CreatedUtc and Owner. - /// - public class CommonPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPartSettings.cs index d1abd8015f6..fc2da7b8f8f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Models/CommonPartSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Contents.Models +namespace OrchardCore.Contents.Models; + +public class CommonPartSettings { - public class CommonPartSettings - { - public bool DisplayDateEditor { get; set; } + public bool DisplayDateEditor { get; set; } - public bool DisplayOwnerEditor { get; set; } - } + public bool DisplayOwnerEditor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Models/FullTextAspectSettings.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Models/FullTextAspectSettings.cs index eb7b49cd1e7..3142bb4322f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Models/FullTextAspectSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Models/FullTextAspectSettings.cs @@ -1,29 +1,28 @@ using System.ComponentModel; -namespace OrchardCore.Contents.Models +namespace OrchardCore.Contents.Models; + +public class FullTextAspectSettings { - public class FullTextAspectSettings - { - /// - /// Whether the content type should use a custom template to add content to the full-text aspect. - /// - public bool IncludeFullTextTemplate { get; set; } + /// + /// Whether the content type should use a custom template to add content to the full-text aspect. + /// + public bool IncludeFullTextTemplate { get; set; } - /// - /// The template used for the full-text aspect. - /// - public string FullTextTemplate { get; set; } + /// + /// The template used for the full-text aspect. + /// + public string FullTextTemplate { get; set; } - /// - /// Whether the body aspect should be added to the full-text aspect. - /// - [DefaultValue(true)] - public bool IncludeBodyAspect { get; set; } = true; + /// + /// Whether the body aspect should be added to the full-text aspect. + /// + [DefaultValue(true)] + public bool IncludeBodyAspect { get; set; } = true; - /// - /// Whether the display text should be added to the full-text aspect. - /// - [DefaultValue(true)] - public bool IncludeDisplayText { get; set; } = true; - } + /// + /// Whether the display text should be added to the full-text aspect. + /// + [DefaultValue(true)] + public bool IncludeDisplayText { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Recipes/ContentStep.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Recipes/ContentStep.cs index 220196b15a9..4e1da72db6f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Recipes/ContentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Recipes/ContentStep.cs @@ -7,44 +7,43 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Contents.Recipes +namespace OrchardCore.Contents.Recipes; + +/// +/// This recipe step creates a set of content items. +/// +public sealed class ContentStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of content items. - /// - public sealed class ContentStep : IRecipeStepHandler + public Task ExecuteAsync(RecipeExecutionContext context) { - public Task ExecuteAsync(RecipeExecutionContext context) + if (!string.Equals(context.Name, "Content", StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(context.Name, "Content", StringComparison.OrdinalIgnoreCase)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - var model = context.Step.ToObject(); - var contentItems = model.Data.ToObject(); + var model = context.Step.ToObject(); + var contentItems = model.Data.ToObject(); - // If the shell is activated there is no migration in progress. - if (ShellScope.Context.IsActivated) - { - var contentManager = ShellScope.Services.GetRequiredService(); - return contentManager.ImportAsync(contentItems); - } + // If the shell is activated there is no migration in progress. + if (ShellScope.Context.IsActivated) + { + var contentManager = ShellScope.Services.GetRequiredService(); + return contentManager.ImportAsync(contentItems); + } - // Otherwise, the import of content items is deferred after all migrations are completed, - // this prevents e.g. a content handler to trigger a workflow before workflows migrations. - ShellScope.AddDeferredTask(scope => - { - var contentManager = scope.ServiceProvider.GetRequiredService(); - return contentManager.ImportAsync(contentItems); - }); + // Otherwise, the import of content items is deferred after all migrations are completed, + // this prevents e.g. a content handler to trigger a workflow before workflows migrations. + ShellScope.AddDeferredTask(scope => + { + var contentManager = scope.ServiceProvider.GetRequiredService(); + return contentManager.ImportAsync(contentItems); + }); - return Task.CompletedTask; - } + return Task.CompletedTask; } +} - public sealed class ContentStepModel - { - public JsonArray Data { get; set; } - } +public sealed class ContentStepModel +{ + public JsonArray Data { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/ContentMethodsProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/ContentMethodsProvider.cs index 90043ffb71b..3b742a5e96e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/ContentMethodsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/ContentMethodsProvider.cs @@ -7,79 +7,78 @@ using OrchardCore.ContentManagement; using OrchardCore.Scripting; -namespace OrchardCore.Contents.Scripting +namespace OrchardCore.Contents.Scripting; + +public class ContentMethodsProvider : IGlobalMethodProvider { - public class ContentMethodsProvider : IGlobalMethodProvider - { - private readonly GlobalMethod _newContentItemMethod; - private readonly GlobalMethod _createContentItemMethod; - private readonly GlobalMethod _updateContentItemMethod; - private readonly GlobalMethod _deleteContentItemMethod; + private readonly GlobalMethod _newContentItemMethod; + private readonly GlobalMethod _createContentItemMethod; + private readonly GlobalMethod _updateContentItemMethod; + private readonly GlobalMethod _deleteContentItemMethod; - public ContentMethodsProvider() + public ContentMethodsProvider() + { + _newContentItemMethod = new GlobalMethod { - _newContentItemMethod = new GlobalMethod + Name = "newContentItem", + Method = serviceProvider => (Func)((contentType) => { - Name = "newContentItem", - Method = serviceProvider => (Func)((contentType) => - { - var contentManager = serviceProvider.GetRequiredService(); - var contentItem = contentManager.NewAsync(contentType).GetAwaiter().GetResult(); + var contentManager = serviceProvider.GetRequiredService(); + var contentItem = contentManager.NewAsync(contentType).GetAwaiter().GetResult(); - return contentItem; - }), - }; + return contentItem; + }), + }; - _createContentItemMethod = new GlobalMethod + _createContentItemMethod = new GlobalMethod + { + Name = "createContentItem", + Method = serviceProvider => (Func)((contentType, publish, properties) => { - Name = "createContentItem", - Method = serviceProvider => (Func)((contentType, publish, properties) => + var contentManager = serviceProvider.GetRequiredService(); + var contentItem = contentManager.NewAsync(contentType).GetAwaiter().GetResult(); + contentItem.Merge(properties); + var result = contentManager.UpdateValidateAndCreateAsync(contentItem, publish == true ? VersionOptions.Published : VersionOptions.Draft).GetAwaiter().GetResult(); + if (result.Succeeded) { - var contentManager = serviceProvider.GetRequiredService(); - var contentItem = contentManager.NewAsync(contentType).GetAwaiter().GetResult(); - contentItem.Merge(properties); - var result = contentManager.UpdateValidateAndCreateAsync(contentItem, publish == true ? VersionOptions.Published : VersionOptions.Draft).GetAwaiter().GetResult(); - if (result.Succeeded) - { - return contentItem; - } - else - { - throw new ValidationException(string.Join(", ", result.Errors)); - } - }), - }; - - _updateContentItemMethod = new GlobalMethod - { - Name = "updateContentItem", - Method = serviceProvider => (Action)((contentItem, properties) => + return contentItem; + } + else { - var contentManager = serviceProvider.GetRequiredService(); - contentItem.Merge(properties, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace }); - contentManager.UpdateAsync(contentItem).GetAwaiter().GetResult(); - var result = contentManager.ValidateAsync(contentItem).GetAwaiter().GetResult(); - if (!result.Succeeded) - { - throw new ValidationException(string.Join(", ", result.Errors)); - } - }), - }; + throw new ValidationException(string.Join(", ", result.Errors)); + } + }), + }; - _deleteContentItemMethod = new GlobalMethod + _updateContentItemMethod = new GlobalMethod + { + Name = "updateContentItem", + Method = serviceProvider => (Action)((contentItem, properties) => { - Name = "deleteContentItem", - Method = serviceProvider => (Action)((contentItem, properties) => + var contentManager = serviceProvider.GetRequiredService(); + contentItem.Merge(properties, new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace }); + contentManager.UpdateAsync(contentItem).GetAwaiter().GetResult(); + var result = contentManager.ValidateAsync(contentItem).GetAwaiter().GetResult(); + if (!result.Succeeded) { - var contentManager = serviceProvider.GetRequiredService(); - contentManager.RemoveAsync(contentItem).GetAwaiter().GetResult(); - }), - }; - } + throw new ValidationException(string.Join(", ", result.Errors)); + } + }), + }; - public IEnumerable GetMethods() + _deleteContentItemMethod = new GlobalMethod { - return new[] { _newContentItemMethod, _createContentItemMethod, _updateContentItemMethod, _deleteContentItemMethod }; - } + Name = "deleteContentItem", + Method = serviceProvider => (Action)((contentItem, properties) => + { + var contentManager = serviceProvider.GetRequiredService(); + contentManager.RemoveAsync(contentItem).GetAwaiter().GetResult(); + }), + }; + } + + public IEnumerable GetMethods() + { + return new[] { _newContentItemMethod, _createContentItemMethod, _updateContentItemMethod, _deleteContentItemMethod }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/Startup.cs index f8b41469a31..15eff714cee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/Startup.cs @@ -2,15 +2,14 @@ using OrchardCore.Modules; using OrchardCore.Scripting; -namespace OrchardCore.Contents.Scripting +namespace OrchardCore.Contents.Scripting; + +[RequireFeatures("OrchardCore.Scripting")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Scripting")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - } + services.AddSingleton(); + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/UrlMethodsProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/UrlMethodsProvider.cs index aeff13830c7..b3ea53e2c68 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/UrlMethodsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Scripting/UrlMethodsProvider.cs @@ -3,43 +3,42 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.Scripting; -namespace OrchardCore.Contents.Scripting +namespace OrchardCore.Contents.Scripting; + +public class UrlMethodsProvider : IGlobalMethodProvider { - public class UrlMethodsProvider : IGlobalMethodProvider - { - private readonly GlobalMethod _getUrlPrefix; + private readonly GlobalMethod _getUrlPrefix; - public UrlMethodsProvider() + public UrlMethodsProvider() + { + _getUrlPrefix = new GlobalMethod { - _getUrlPrefix = new GlobalMethod + Name = "getUrlPrefix", + Method = serviceProvider => (string path, bool? escaped) => { - Name = "getUrlPrefix", - Method = serviceProvider => (string path, bool? escaped) => + var httpContextAccessor = serviceProvider.GetRequiredService(); + + var pathBase = httpContextAccessor.HttpContext?.Request.PathBase ?? PathString.Empty; + if (!pathBase.HasValue) + { + pathBase = "/"; + } + + path = path?.Trim(' ', '/') ?? string.Empty; + if (path.Length > 0) + { + pathBase = pathBase.Add($"/{path}"); + } + + if (escaped.HasValue && escaped.Value) { - var httpContextAccessor = serviceProvider.GetRequiredService(); - - var pathBase = httpContextAccessor.HttpContext?.Request.PathBase ?? PathString.Empty; - if (!pathBase.HasValue) - { - pathBase = "/"; - } - - path = path?.Trim(' ', '/') ?? string.Empty; - if (path.Length > 0) - { - pathBase = pathBase.Add($"/{path}"); - } - - if (escaped.HasValue && escaped.Value) - { - return pathBase.ToString(); - } - - return pathBase.Value; - }, - }; - } - - public IEnumerable GetMethods() => new[] { _getUrlPrefix }; + return pathBase.ToString(); + } + + return pathBase.Value; + }, + }; } + + public IEnumerable GetMethods() => new[] { _getUrlPrefix }; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Security/ContentTypeAuthorizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Security/ContentTypeAuthorizationHandler.cs index 0a597199109..b103c2200ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Security/ContentTypeAuthorizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Security/ContentTypeAuthorizationHandler.cs @@ -7,99 +7,98 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.Contents.Security +namespace OrchardCore.Contents.Security; + +public class ContentTypeAuthorizationHandler : AuthorizationHandler { - public class ContentTypeAuthorizationHandler : AuthorizationHandler + private readonly IServiceProvider _serviceProvider; + private IAuthorizationService _authorizationService; + + public ContentTypeAuthorizationHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; - private IAuthorizationService _authorizationService; + _serviceProvider = serviceProvider; + } - public ContentTypeAuthorizationHandler(IServiceProvider serviceProvider) + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _serviceProvider = serviceProvider; + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + // If we are not evaluating a ContentItem then return. + if (context.Resource == null) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } - - // If we are not evaluating a ContentItem then return. - if (context.Resource == null) - { - return; - } - - var contentItem = context.Resource as ContentItem; + return; + } - Permission permission = null; + var contentItem = context.Resource as ContentItem; - if (contentItem != null) - { - var ownerVariation = GetOwnerVariation(requirement.Permission); - - if (OwnerVariationExists(requirement.Permission) && HasOwnership(context.User, contentItem)) - { - permission = ownerVariation; - } - } + Permission permission = null; - var contentTypePermission = ContentTypePermissionsHelper.ConvertToDynamicPermission(permission ?? requirement.Permission); + if (contentItem != null) + { + var ownerVariation = GetOwnerVariation(requirement.Permission); - if (contentTypePermission != null) + if (OwnerVariationExists(requirement.Permission) && HasOwnership(context.User, contentItem)) { - // The resource can be a content type name - var contentType = contentItem != null - ? contentItem.ContentType - : context.Resource.ToString() - ; - - if (!string.IsNullOrEmpty(contentType)) - { - permission = ContentTypePermissionsHelper.CreateDynamicPermission(contentTypePermission, contentType); - } + permission = ownerVariation; } + } - if (permission == null) - { - return; - } + var contentTypePermission = ContentTypePermissionsHelper.ConvertToDynamicPermission(permission ?? requirement.Permission); - // Lazy load to prevent circular dependencies - _authorizationService ??= _serviceProvider.GetService(); + if (contentTypePermission != null) + { + // The resource can be a content type name + var contentType = contentItem != null + ? contentItem.ContentType + : context.Resource.ToString() + ; - if (await _authorizationService.AuthorizeAsync(context.User, permission)) + if (!string.IsNullOrEmpty(contentType)) { - context.Succeed(requirement); + permission = ContentTypePermissionsHelper.CreateDynamicPermission(contentTypePermission, contentType); } } - private static Permission GetOwnerVariation(Permission permission) + if (permission == null) { - if (CommonPermissions.OwnerPermissionsByName.TryGetValue(permission.Name, out var ownerVariation)) - { - return ownerVariation; - } - - return null; + return; } - private static bool HasOwnership(ClaimsPrincipal user, ContentItem content) + // Lazy load to prevent circular dependencies + _authorizationService ??= _serviceProvider.GetService(); + + if (await _authorizationService.AuthorizeAsync(context.User, permission)) { - if (user == null || content == null) - { - return false; - } + context.Succeed(requirement); + } + } - return user.FindFirstValue(ClaimTypes.NameIdentifier) == content.Owner; + private static Permission GetOwnerVariation(Permission permission) + { + if (CommonPermissions.OwnerPermissionsByName.TryGetValue(permission.Name, out var ownerVariation)) + { + return ownerVariation; } - private static bool OwnerVariationExists(Permission permission) + return null; + } + + private static bool HasOwnership(ClaimsPrincipal user, ContentItem content) + { + if (user == null || content == null) { - return GetOwnerVariation(permission) != null; + return false; } + + return user.FindFirstValue(ClaimTypes.NameIdentifier) == content.Owner; + } + + private static bool OwnerVariationExists(Permission permission) + { + return GetOwnerVariation(permission) != null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentItemIdHandleProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentItemIdHandleProvider.cs index 9aae77e36cd..55cad1090d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentItemIdHandleProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentItemIdHandleProvider.cs @@ -2,22 +2,21 @@ using System.Threading.Tasks; using OrchardCore.ContentManagement; -namespace OrchardCore.Contents.Services +namespace OrchardCore.Contents.Services; + +public class ContentItemIdHandleProvider : IContentHandleProvider { - public class ContentItemIdHandleProvider : IContentHandleProvider - { - public int Order => 0; + public int Order => 0; - public Task GetContentItemIdAsync(string handle) + public Task GetContentItemIdAsync(string handle) + { + if (handle.StartsWith("contentitemid:", StringComparison.OrdinalIgnoreCase)) { - if (handle.StartsWith("contentitemid:", StringComparison.OrdinalIgnoreCase)) - { - var contentItemId = handle[14..]; + var contentItemId = handle[14..]; - return Task.FromResult(contentItemId); - } - - return Task.FromResult(null); + return Task.FromResult(contentItemId); } + + return Task.FromResult(null); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentTypeFilterNode.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentTypeFilterNode.cs index b649aaa00c6..6ae7190619c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentTypeFilterNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Services/ContentTypeFilterNode.cs @@ -1,20 +1,19 @@ using YesSql.Filters.Abstractions.Nodes; -namespace OrchardCore.Contents.Services +namespace OrchardCore.Contents.Services; + +/// +/// Provides a content type node is used when a filter has not been selected. +/// +public class ContentTypeFilterNode : TermOperationNode { - /// - /// Provides a content type node is used when a filter has not been selected. - /// - public class ContentTypeFilterNode : TermOperationNode + public ContentTypeFilterNode(string selectedContentType) : base("type", new UnaryNode(selectedContentType, OperateNodeQuotes.None)) { - public ContentTypeFilterNode(string selectedContentType) : base("type", new UnaryNode(selectedContentType, OperateNodeQuotes.None)) - { - } + } - public override string ToNormalizedString() - => string.Empty; + public override string ToNormalizedString() + => string.Empty; - public override string ToString() - => string.Empty; - } + public override string ToString() + => string.Empty; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterParser.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterParser.cs index daab0845412..304cebe1524 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterParser.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterParser.cs @@ -1,18 +1,17 @@ using OrchardCore.ContentManagement; using YesSql.Filters.Query; -namespace OrchardCore.Contents.Services -{ - public class DefaultContentsAdminListFilterParser : IContentsAdminListFilterParser - { - private readonly IQueryParser _parser; +namespace OrchardCore.Contents.Services; - public DefaultContentsAdminListFilterParser(IQueryParser parser) - { - _parser = parser; - } +public class DefaultContentsAdminListFilterParser : IContentsAdminListFilterParser +{ + private readonly IQueryParser _parser; - public QueryFilterResult Parse(string text) - => _parser.Parse(text); + public DefaultContentsAdminListFilterParser(IQueryParser parser) + { + _parser = parser; } + + public QueryFilterResult Parse(string text) + => _parser.Parse(text); } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterProvider.cs index 3ff5c6a02cc..fcd89645c28 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Services/DefaultContentsAdminListFilterProvider.cs @@ -15,246 +15,245 @@ using YesSql.Filters.Query; using YesSql.Services; -namespace OrchardCore.Contents.Services +namespace OrchardCore.Contents.Services; + +public class DefaultContentsAdminListFilterProvider : IContentsAdminListFilterProvider { - public class DefaultContentsAdminListFilterProvider : IContentsAdminListFilterProvider + public void Build(QueryEngineBuilder builder) { - public void Build(QueryEngineBuilder builder) - { - builder - .WithNamedTerm("status", builder => builder - .OneCondition((val, query, ctx) => + builder + .WithNamedTerm("status", builder => builder + .OneCondition((val, query, ctx) => + { + var context = (ContentQueryContext)ctx; + if (Enum.TryParse(val, true, out var contentsStatus)) { - var context = (ContentQueryContext)ctx; - if (Enum.TryParse(val, true, out var contentsStatus)) - { - switch (contentsStatus) - { - case ContentsStatus.Draft: - query.With(x => x.Latest && !x.Published); - break; - case ContentsStatus.Published: - query.With(x => x.Published); - break; - case ContentsStatus.Owner: - var httpContextAccessor = context.ServiceProvider.GetRequiredService(); - var userNameIdentifier = httpContextAccessor.HttpContext.User?.FindFirstValue(ClaimTypes.NameIdentifier); - query.With(x => x.Owner == userNameIdentifier && x.Latest); - break; - case ContentsStatus.AllVersions: - query.With(x => x.Latest); - break; - default: - query.With(x => x.Latest); - break; - } - } - else + switch (contentsStatus) { - // Draft is the default value. - query.With(x => x.Latest); + case ContentsStatus.Draft: + query.With(x => x.Latest && !x.Published); + break; + case ContentsStatus.Published: + query.With(x => x.Published); + break; + case ContentsStatus.Owner: + var httpContextAccessor = context.ServiceProvider.GetRequiredService(); + var userNameIdentifier = httpContextAccessor.HttpContext.User?.FindFirstValue(ClaimTypes.NameIdentifier); + query.With(x => x.Owner == userNameIdentifier && x.Latest); + break; + case ContentsStatus.AllVersions: + query.With(x => x.Latest); + break; + default: + query.With(x => x.Latest); + break; } + } + else + { + // Draft is the default value. + query.With(x => x.Latest); + } - return new ValueTask>(query); - }) - .MapTo((val, model) => + return new ValueTask>(query); + }) + .MapTo((val, model) => + { + if (Enum.TryParse(val, true, out var contentsStatus)) { - if (Enum.TryParse(val, true, out var contentsStatus)) - { - model.ContentsStatus = contentsStatus; - } - }) - .MapFrom((model) => + model.ContentsStatus = contentsStatus; + } + }) + .MapFrom((model) => + { + if (model.ContentsStatus != ContentsStatus.Latest) { - if (model.ContentsStatus != ContentsStatus.Latest) - { - return (true, model.ContentsStatus.ToString()); - } + return (true, model.ContentsStatus.ToString()); + } - return (false, string.Empty); - }) - .AlwaysRun() - ) - .WithNamedTerm("sort", builder => builder - .OneCondition((val, query) => + return (false, string.Empty); + }) + .AlwaysRun() + ) + .WithNamedTerm("sort", builder => builder + .OneCondition((val, query) => + { + if (Enum.TryParse(val, true, out var contentsOrder)) { - if (Enum.TryParse(val, true, out var contentsOrder)) + switch (contentsOrder) { - switch (contentsOrder) - { - case ContentsOrder.Modified: - query.With().OrderByDescending(cr => cr.ModifiedUtc).ThenBy(cr => cr.Id); - break; - case ContentsOrder.Published: - query.With().OrderByDescending(cr => cr.PublishedUtc).ThenBy(cr => cr.Id); - break; - case ContentsOrder.Created: - query.With().OrderByDescending(cr => cr.CreatedUtc).ThenBy(cr => cr.Id); - break; - case ContentsOrder.Title: - query.With().OrderBy(cr => cr.DisplayText).ThenBy(cr => cr.Id); - break; - }; - } - else - { - // Modified is a default value and applied when there is no filter. - query.With().OrderByDescending(cr => cr.ModifiedUtc).ThenBy(cr => cr.Id); - } + case ContentsOrder.Modified: + query.With().OrderByDescending(cr => cr.ModifiedUtc).ThenBy(cr => cr.Id); + break; + case ContentsOrder.Published: + query.With().OrderByDescending(cr => cr.PublishedUtc).ThenBy(cr => cr.Id); + break; + case ContentsOrder.Created: + query.With().OrderByDescending(cr => cr.CreatedUtc).ThenBy(cr => cr.Id); + break; + case ContentsOrder.Title: + query.With().OrderBy(cr => cr.DisplayText).ThenBy(cr => cr.Id); + break; + }; + } + else + { + // Modified is a default value and applied when there is no filter. + query.With().OrderByDescending(cr => cr.ModifiedUtc).ThenBy(cr => cr.Id); + } - return query; - }) - .MapTo((val, model) => + return query; + }) + .MapTo((val, model) => + { + if (Enum.TryParse(val, true, out var contentsOrder)) { - if (Enum.TryParse(val, true, out var contentsOrder)) - { - model.OrderBy = contentsOrder; - } - }) - .MapFrom((model) => + model.OrderBy = contentsOrder; + } + }) + .MapFrom((model) => + { + if (model.OrderBy != ContentsOrder.Modified) { - if (model.OrderBy != ContentsOrder.Modified) - { - return (true, model.OrderBy.ToString()); - } + return (true, model.OrderBy.ToString()); + } - return (false, string.Empty); - }) - .AlwaysRun() - ) - .WithNamedTerm("type", builder => builder - .OneCondition(async (contentType, query, ctx) => - { - var context = (ContentQueryContext)ctx; - var httpContextAccessor = context.ServiceProvider.GetRequiredService(); - var authorizationService = context.ServiceProvider.GetRequiredService(); - var contentDefinitionManager = context.ServiceProvider.GetRequiredService(); - var user = httpContextAccessor.HttpContext.User; - var userNameIdentifier = user?.FindFirstValue(ClaimTypes.NameIdentifier); + return (false, string.Empty); + }) + .AlwaysRun() + ) + .WithNamedTerm("type", builder => builder + .OneCondition(async (contentType, query, ctx) => + { + var context = (ContentQueryContext)ctx; + var httpContextAccessor = context.ServiceProvider.GetRequiredService(); + var authorizationService = context.ServiceProvider.GetRequiredService(); + var contentDefinitionManager = context.ServiceProvider.GetRequiredService(); + var user = httpContextAccessor.HttpContext.User; + var userNameIdentifier = user?.FindFirstValue(ClaimTypes.NameIdentifier); - // Filter for a specific type. - if (!string.IsNullOrEmpty(contentType)) + // Filter for a specific type. + if (!string.IsNullOrEmpty(contentType)) + { + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentType); + if (contentTypeDefinition != null) { - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentType); - if (contentTypeDefinition != null) - { - // We display a specific type even if it's not listable so that admin pages - // can reuse the Content list page for specific types. - - // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. - if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, contentTypeDefinition.Name, owner: null)) - { - return query.With(x => x.ContentType == contentType); - } + // We display a specific type even if it's not listable so that admin pages + // can reuse the Content list page for specific types. - return query.With(x => x.ContentType == contentType && x.Owner == userNameIdentifier); + // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. + if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, contentTypeDefinition.Name, owner: null)) + { + return query.With(x => x.ContentType == contentType); } - // At this point, the given contentType is invalid. Ignore it. + return query.With(x => x.ContentType == contentType && x.Owner == userNameIdentifier); } - var listAnyContentTypes = new List(); - var listOwnContentTypes = new List(); + // At this point, the given contentType is invalid. Ignore it. + } - foreach (var ctd in await contentDefinitionManager.ListTypeDefinitionsAsync()) + var listAnyContentTypes = new List(); + var listOwnContentTypes = new List(); + + foreach (var ctd in await contentDefinitionManager.ListTypeDefinitionsAsync()) + { + if (!ctd.IsListable()) { - if (!ctd.IsListable()) - { - continue; - } + continue; + } - // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. - if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, ctd.Name, owner: null)) - { - listAnyContentTypes.Add(ctd.Name); + // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. + if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, ctd.Name, owner: null)) + { + listAnyContentTypes.Add(ctd.Name); - continue; - } + continue; + } - // It is important to pass the current user ID to the owner parameter. This will check if the user can view their own content. - if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, ctd.Name, userNameIdentifier)) - { - listOwnContentTypes.Add(ctd.Name); + // It is important to pass the current user ID to the owner parameter. This will check if the user can view their own content. + if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, ctd.Name, userNameIdentifier)) + { + listOwnContentTypes.Add(ctd.Name); - continue; - } + continue; } + } - return query.With().Where(x => x.ContentType.IsIn(listAnyContentTypes) || (x.ContentType.IsIn(listOwnContentTypes) && x.Owner == userNameIdentifier)); - }) - .MapTo((val, model) => + return query.With().Where(x => x.ContentType.IsIn(listAnyContentTypes) || (x.ContentType.IsIn(listOwnContentTypes) && x.Owner == userNameIdentifier)); + }) + .MapTo((val, model) => + { + if (!string.IsNullOrEmpty(val)) { - if (!string.IsNullOrEmpty(val)) - { - model.SelectedContentType = val; - } - }) - .MapFrom((model) => + model.SelectedContentType = val; + } + }) + .MapFrom((model) => + { + if (!string.IsNullOrEmpty(model.SelectedContentType)) { - if (!string.IsNullOrEmpty(model.SelectedContentType)) - { - return (true, model.SelectedContentType); - } + return (true, model.SelectedContentType); + } - return (false, string.Empty); - }) - .AlwaysRun() - ) - .WithNamedTerm("stereotype", builder => builder - .OneCondition(async (stereotype, query, ctx) => + return (false, string.Empty); + }) + .AlwaysRun() + ) + .WithNamedTerm("stereotype", builder => builder + .OneCondition(async (stereotype, query, ctx) => + { + var context = (ContentQueryContext)ctx; + var httpContextAccessor = context.ServiceProvider.GetRequiredService(); + var authorizationService = context.ServiceProvider.GetRequiredService(); + var contentDefinitionManager = context.ServiceProvider.GetRequiredService(); + + // Filter for a specific stereotype. + if (!string.IsNullOrEmpty(stereotype)) { - var context = (ContentQueryContext)ctx; - var httpContextAccessor = context.ServiceProvider.GetRequiredService(); - var authorizationService = context.ServiceProvider.GetRequiredService(); - var contentDefinitionManager = context.ServiceProvider.GetRequiredService(); + var contentTypeDefinitionNames = (await contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(definition => definition.StereotypeEquals(stereotype, StringComparison.OrdinalIgnoreCase)) + .Select(definition => definition.Name) + .ToList(); - // Filter for a specific stereotype. - if (!string.IsNullOrEmpty(stereotype)) + // We display a specific type even if it's not listable so that admin pages + // can reuse the content list page for specific types. + + if (contentTypeDefinitionNames.Count > 0) { - var contentTypeDefinitionNames = (await contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(definition => definition.StereotypeEquals(stereotype, StringComparison.OrdinalIgnoreCase)) - .Select(definition => definition.Name) - .ToList(); + var user = httpContextAccessor.HttpContext.User; + var userNameIdentifier = user?.FindFirstValue(ClaimTypes.NameIdentifier); - // We display a specific type even if it's not listable so that admin pages - // can reuse the content list page for specific types. + var viewAll = new List(); + var viewOwn = new List(); - if (contentTypeDefinitionNames.Count > 0) + foreach (var contentTypeDefinitionName in contentTypeDefinitionNames) { - var user = httpContextAccessor.HttpContext.User; - var userNameIdentifier = user?.FindFirstValue(ClaimTypes.NameIdentifier); - - var viewAll = new List(); - var viewOwn = new List(); - - foreach (var contentTypeDefinitionName in contentTypeDefinitionNames) + // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. + if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, contentTypeDefinitionName, owner: null)) { - // It is important to pass null to the owner parameter. This will check if the user can view content that belongs to others. - if (await authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.ViewContent, contentTypeDefinitionName, owner: null)) - { - viewAll.Add(contentTypeDefinitionName); - - continue; - } + viewAll.Add(contentTypeDefinitionName); - viewOwn.Add(contentTypeDefinitionName); + continue; } - return query.With(x => x.ContentType.IsIn(viewAll) || (x.ContentType.IsIn(viewOwn) && x.Owner == userNameIdentifier)); + viewOwn.Add(contentTypeDefinitionName); } - // At this point, the given stereotype is invalid. Ignore it. + return query.With(x => x.ContentType.IsIn(viewAll) || (x.ContentType.IsIn(viewOwn) && x.Owner == userNameIdentifier)); } - return query; - }) + // At this point, the given stereotype is invalid. Ignore it. + } + + return query; + }) + ) + .WithDefaultTerm(ContentsAdminListFilterOptions.DefaultTermName, builder => builder + .ManyCondition( + (val, query) => query.With(x => x.DisplayText.Contains(val)), + (val, query) => query.With(x => x.DisplayText.NotContains(val)) ) - .WithDefaultTerm(ContentsAdminListFilterOptions.DefaultTermName, builder => builder - .ManyCondition( - (val, query) => query.With(x => x.DisplayText.Contains(val)), - (val, query) => query.With(x => x.DisplayText.NotContains(val)) - ) - ); - } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Settings/CommonPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Settings/CommonPartSettingsDisplayDriver.cs index 267b9a69766..5956c5db615 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Settings/CommonPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Settings/CommonPartSettingsDisplayDriver.cs @@ -6,33 +6,32 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Lists.Settings +namespace OrchardCore.Lists.Settings; + +public sealed class CommonPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class CommonPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("CommonPartSettings_Edit", model => { - return Initialize("CommonPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); - model.DisplayDateEditor = settings.DisplayDateEditor; - model.DisplayOwnerEditor = settings.DisplayOwnerEditor; - }).Location("Content"); - } + var settings = contentTypePartDefinition.GetSettings(); + model.DisplayDateEditor = settings.DisplayDateEditor; + model.DisplayOwnerEditor = settings.DisplayOwnerEditor; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new CommonPartSettingsViewModel(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new CommonPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(new CommonPartSettings - { - DisplayDateEditor = model.DisplayDateEditor, - DisplayOwnerEditor = model.DisplayOwnerEditor, - }); + context.Builder.WithSettings(new CommonPartSettings + { + DisplayDateEditor = model.DisplayDateEditor, + DisplayOwnerEditor = model.DisplayOwnerEditor, + }); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Settings/FullTextAspectSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Settings/FullTextAspectSettingsDisplayDriver.cs index bfd15e0d317..edd89eb4228 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Settings/FullTextAspectSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Settings/FullTextAspectSettingsDisplayDriver.cs @@ -8,64 +8,63 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Liquid; -namespace OrchardCore.Contents.Settings +namespace OrchardCore.Contents.Settings; + +public sealed class FullTextAspectSettingsDisplayDriver : ContentTypeDefinitionDisplayDriver { - public sealed class FullTextAspectSettingsDisplayDriver : ContentTypeDefinitionDisplayDriver - { - private readonly ILiquidTemplateManager _templateManager; + private readonly ILiquidTemplateManager _templateManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public FullTextAspectSettingsDisplayDriver( - ILiquidTemplateManager templateManager, - IStringLocalizer localizer) - { - _templateManager = templateManager; - S = localizer; - } + public FullTextAspectSettingsDisplayDriver( + ILiquidTemplateManager templateManager, + IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } - public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypeDefinition contentTypeDefinition, BuildEditorContext context) + { + return Initialize("FullTextAspectSettings_Edit", model => { - return Initialize("FullTextAspectSettings_Edit", model => - { - var settings = contentTypeDefinition.GetSettings(); + var settings = contentTypeDefinition.GetSettings(); - model.IncludeFullTextTemplate = settings.IncludeFullTextTemplate; - model.FullTextTemplate = settings.FullTextTemplate; - model.IncludeDisplayText = settings.IncludeDisplayText; - model.IncludeBodyAspect = settings.IncludeBodyAspect; - }).Location("Content:6"); - } + model.IncludeFullTextTemplate = settings.IncludeFullTextTemplate; + model.FullTextTemplate = settings.FullTextTemplate; + model.IncludeDisplayText = settings.IncludeDisplayText; + model.IncludeBodyAspect = settings.IncludeBodyAspect; + }).Location("Content:6"); + } - public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) - { - var model = new FullTextAspectSettingsViewModel(); + public override async Task UpdateAsync(ContentTypeDefinition contentTypeDefinition, UpdateTypeEditorContext context) + { + var model = new FullTextAspectSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.IncludeFullTextTemplate, - m => m.FullTextTemplate, - m => m.IncludeDisplayText, - m => m.IncludeBodyAspect); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.IncludeFullTextTemplate, + m => m.FullTextTemplate, + m => m.IncludeDisplayText, + m => m.IncludeBodyAspect); - if (!string.IsNullOrEmpty(model.FullTextTemplate) && !_templateManager.Validate(model.FullTextTemplate, out var errors)) - { - context.Updater.ModelState.AddModelError( - nameof(model.FullTextTemplate), - S["Full-text doesn't contain a valid Liquid expression. Details: {0}", - string.Join(' ', errors)]); - } - else + if (!string.IsNullOrEmpty(model.FullTextTemplate) && !_templateManager.Validate(model.FullTextTemplate, out var errors)) + { + context.Updater.ModelState.AddModelError( + nameof(model.FullTextTemplate), + S["Full-text doesn't contain a valid Liquid expression. Details: {0}", + string.Join(' ', errors)]); + } + else + { + context.Builder.WithSettings(new FullTextAspectSettings { - context.Builder.WithSettings(new FullTextAspectSettings - { - IncludeFullTextTemplate = model.IncludeFullTextTemplate, - FullTextTemplate = model.FullTextTemplate, - IncludeDisplayText = model.IncludeDisplayText, - IncludeBodyAspect = model.IncludeBodyAspect - }); - } - - return Edit(contentTypeDefinition, context); + IncludeFullTextTemplate = model.IncludeFullTextTemplate, + FullTextTemplate = model.FullTextTemplate, + IncludeDisplayText = model.IncludeDisplayText, + IncludeBodyAspect = model.IncludeBodyAspect + }); } + + return Edit(contentTypeDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Shapes.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Shapes.cs index de392d1385b..b342045fb67 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Shapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Shapes.cs @@ -7,94 +7,93 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Utilities; -namespace OrchardCore.Contents +namespace OrchardCore.Contents; + +public class Shapes : ShapeTableProvider { - public class Shapes : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("Content") - .OnDisplaying(displaying => + builder.Describe("Content") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; + var contentItem = shape.GetProperty("ContentItem"); + + if (contentItem != null) { - var shape = displaying.Shape; - var contentItem = shape.GetProperty("ContentItem"); + // Alternates in order of specificity. + // Display type > content type > specific content > display type for a content type > display type for specific content + // BasicShapeTemplateHarvester.Adjust will then adjust the template name - if (contentItem != null) - { - // Alternates in order of specificity. - // Display type > content type > specific content > display type for a content type > display type for specific content - // BasicShapeTemplateHarvester.Adjust will then adjust the template name + // Content__[DisplayType] e.g. Content-Summary + displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType.EncodeAlternateElement()); - // Content__[DisplayType] e.g. Content-Summary - displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType.EncodeAlternateElement()); + var encodedContentType = contentItem.ContentType.EncodeAlternateElement(); - var encodedContentType = contentItem.ContentType.EncodeAlternateElement(); + // Content__[ContentType] e.g. Content-BlogPost, + displaying.Shape.Metadata.Alternates.Add("Content__" + encodedContentType); - // Content__[ContentType] e.g. Content-BlogPost, - displaying.Shape.Metadata.Alternates.Add("Content__" + encodedContentType); + // Content__[Id] e.g. Content-42, + displaying.Shape.Metadata.Alternates.Add("Content__" + contentItem.Id); - // Content__[Id] e.g. Content-42, - displaying.Shape.Metadata.Alternates.Add("Content__" + contentItem.Id); + // Content_[DisplayType]__[ContentType] e.g. Content-BlogPost.Summary + displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType + "__" + encodedContentType); - // Content_[DisplayType]__[ContentType] e.g. Content-BlogPost.Summary - displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType + "__" + encodedContentType); + // Content_[DisplayType]__[Id] e.g. Content-42.Summary + displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType + "__" + contentItem.Id); + } + }); - // Content_[DisplayType]__[Id] e.g. Content-42.Summary - displaying.Shape.Metadata.Alternates.Add("Content_" + displaying.Shape.Metadata.DisplayType + "__" + contentItem.Id); - } - }); + // This shapes provides a way to lazily load a content item render it in any display type. + builder.Describe("ContentItem") + .OnProcessing(async context => + { + var content = context.Shape; + var handle = content.GetProperty("Handle"); + var displayType = content.GetProperty("DisplayType"); + var alternate = content.GetProperty("Alternate"); - // This shapes provides a way to lazily load a content item render it in any display type. - builder.Describe("ContentItem") - .OnProcessing(async context => + if (string.IsNullOrEmpty(handle)) { - var content = context.Shape; - var handle = content.GetProperty("Handle"); - var displayType = content.GetProperty("DisplayType"); - var alternate = content.GetProperty("Alternate"); - + // This code is provided for backwards compatibility and can be removed in a future version. + handle = content.GetProperty("Alias"); if (string.IsNullOrEmpty(handle)) { - // This code is provided for backwards compatibility and can be removed in a future version. - handle = content.GetProperty("Alias"); - if (string.IsNullOrEmpty(handle)) - { - return; - } + return; } + } - var contentManager = context.ServiceProvider.GetRequiredService(); - var handleManager = context.ServiceProvider.GetRequiredService(); - var displayManager = context.ServiceProvider.GetRequiredService(); - var updateModelAccessor = context.ServiceProvider.GetRequiredService(); + var contentManager = context.ServiceProvider.GetRequiredService(); + var handleManager = context.ServiceProvider.GetRequiredService(); + var displayManager = context.ServiceProvider.GetRequiredService(); + var updateModelAccessor = context.ServiceProvider.GetRequiredService(); - var contentItemId = await handleManager.GetContentItemIdAsync(handle); + var contentItemId = await handleManager.GetContentItemIdAsync(handle); - if (string.IsNullOrEmpty(contentItemId)) - { - return; - } + if (string.IsNullOrEmpty(contentItemId)) + { + return; + } - var contentItem = await contentManager.GetAsync(contentItemId); + var contentItem = await contentManager.GetAsync(contentItemId); - if (contentItem == null) - { - return; - } + if (contentItem == null) + { + return; + } - content.Properties["ContentItem"] = contentItem; + content.Properties["ContentItem"] = contentItem; - var displayShape = await displayManager.BuildDisplayAsync(contentItem, updateModelAccessor.ModelUpdater, displayType); + var displayShape = await displayManager.BuildDisplayAsync(contentItem, updateModelAccessor.ModelUpdater, displayType); - if (!string.IsNullOrEmpty(alternate)) - { - displayShape.Metadata.Alternates.Add(alternate); - } + if (!string.IsNullOrEmpty(alternate)) + { + displayShape.Metadata.Alternates.Add(alternate); + } - await context.Shape.AddAsync(displayShape, string.Empty); - }); + await context.Shape.AddAsync(displayShape, string.Empty); + }); - return ValueTask.CompletedTask; - } + return ValueTask.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceBuilder.cs index 3f2fef1690e..15c34394a84 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceBuilder.cs @@ -10,161 +10,160 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class ContentTypesSitemapSourceBuilder : SitemapSourceBuilderBase { - public class ContentTypesSitemapSourceBuilder : SitemapSourceBuilderBase + private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; + + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + private readonly IContentManager _contentManager; + private readonly IContentItemsQueryProvider _contentItemsQueryProvider; + private readonly IEnumerable _sitemapContentItemExtendedMetadataProviders; + + public ContentTypesSitemapSourceBuilder( + IRouteableContentTypeCoordinator routeableContentTypeCoordinator, + IContentManager contentManager, + IContentItemsQueryProvider contentItemsQueryProvider, + IEnumerable sitemapContentItemExtendedMetadataProviders + ) + { + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + _contentManager = contentManager; + _contentItemsQueryProvider = contentItemsQueryProvider; + _sitemapContentItemExtendedMetadataProviders = sitemapContentItemExtendedMetadataProviders; + } + + public override async Task BuildSourceAsync(ContentTypesSitemapSource source, SitemapBuilderContext context) { - private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; - - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; - private readonly IContentManager _contentManager; - private readonly IContentItemsQueryProvider _contentItemsQueryProvider; - private readonly IEnumerable _sitemapContentItemExtendedMetadataProviders; - - public ContentTypesSitemapSourceBuilder( - IRouteableContentTypeCoordinator routeableContentTypeCoordinator, - IContentManager contentManager, - IContentItemsQueryProvider contentItemsQueryProvider, - IEnumerable sitemapContentItemExtendedMetadataProviders - ) + var queryContext = new ContentItemsQueryContext(); + await _contentItemsQueryProvider.GetContentItemsAsync(source, queryContext); + + foreach (var sciemp in _sitemapContentItemExtendedMetadataProviders) { - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; - _contentManager = contentManager; - _contentItemsQueryProvider = contentItemsQueryProvider; - _sitemapContentItemExtendedMetadataProviders = sitemapContentItemExtendedMetadataProviders; + context.Response.ResponseElement.Add(sciemp.GetExtendedAttribute); } - public override async Task BuildSourceAsync(ContentTypesSitemapSource source, SitemapBuilderContext context) + foreach (var contentItem in queryContext.ContentItems) { - var queryContext = new ContentItemsQueryContext(); - await _contentItemsQueryProvider.GetContentItemsAsync(source, queryContext); + var url = new XElement(_namespace + "url"); - foreach (var sciemp in _sitemapContentItemExtendedMetadataProviders) + if (await BuildUrlsetMetadataAsync(source, context, queryContext, contentItem, url)) { - context.Response.ResponseElement.Add(sciemp.GetExtendedAttribute); - } - - foreach (var contentItem in queryContext.ContentItems) - { - var url = new XElement(_namespace + "url"); - - if (await BuildUrlsetMetadataAsync(source, context, queryContext, contentItem, url)) - { - context.Response.ResponseElement.Add(url); - } + context.Response.ResponseElement.Add(url); } } + } - private async Task BuildUrlsetMetadataAsync(ContentTypesSitemapSource source, SitemapBuilderContext context, ContentItemsQueryContext queryContext, ContentItem contentItem, XElement url) + private async Task BuildUrlsetMetadataAsync(ContentTypesSitemapSource source, SitemapBuilderContext context, ContentItemsQueryContext queryContext, ContentItem contentItem, XElement url) + { + if (await BuildUrlAsync(context, contentItem, url)) { - if (await BuildUrlAsync(context, contentItem, url)) + if (await BuildExtendedMetadataAsync(context, queryContext, contentItem, url)) { - if (await BuildExtendedMetadataAsync(context, queryContext, contentItem, url)) - { - PopulateLastMod(contentItem, url); - await PopulateChangeFrequencyPriority(source, contentItem, url); - return true; - } - - return false; - }; + PopulateLastMod(contentItem, url); + await PopulateChangeFrequencyPriority(source, contentItem, url); + return true; + } return false; - } + }; - private async Task BuildExtendedMetadataAsync(SitemapBuilderContext context, ContentItemsQueryContext queryContext, ContentItem contentItem, XElement url) + return false; + } + + private async Task BuildExtendedMetadataAsync(SitemapBuilderContext context, ContentItemsQueryContext queryContext, ContentItem contentItem, XElement url) + { + var succeeded = true; + foreach (var sc in _sitemapContentItemExtendedMetadataProviders) { - var succeeded = true; - foreach (var sc in _sitemapContentItemExtendedMetadataProviders) + if (!await sc.ApplyExtendedMetadataAsync(context, queryContext, contentItem, url)) { - if (!await sc.ApplyExtendedMetadataAsync(context, queryContext, contentItem, url)) - { - succeeded = false; - } + succeeded = false; } - return succeeded; } + return succeeded; + } + + private async Task BuildUrlAsync(SitemapBuilderContext context, ContentItem contentItem, XElement url) + { + var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(contentItem); + if (sitemapMetadataAspect.Exclude) + { + return false; + } + + var locValue = await _routeableContentTypeCoordinator.GetRouteAsync(context, contentItem); + + var loc = new XElement(_namespace + "loc"); + loc.Add(locValue); + url.Add(loc); + return true; + } + + private async Task PopulateChangeFrequencyPriority(ContentTypesSitemapSource source, ContentItem contentItem, XElement url) + { + var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(contentItem); - private async Task BuildUrlAsync(SitemapBuilderContext context, ContentItem contentItem, XElement url) + var changeFrequencyValue = sitemapMetadataAspect.ChangeFrequency; + if (string.IsNullOrEmpty(changeFrequencyValue)) { - var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(contentItem); - if (sitemapMetadataAspect.Exclude) + if (source.IndexAll) { - return false; + changeFrequencyValue = source.ChangeFrequency.ToString(); } + else if (source.LimitItems) + { + changeFrequencyValue = source.LimitedContentType.ChangeFrequency.ToString(); + } + else + { + var sitemapEntry = source.ContentTypes + .FirstOrDefault(ct => string.Equals(ct.ContentTypeName, contentItem.ContentType, StringComparison.Ordinal)); - var locValue = await _routeableContentTypeCoordinator.GetRouteAsync(context, contentItem); - - var loc = new XElement(_namespace + "loc"); - loc.Add(locValue); - url.Add(loc); - return true; + changeFrequencyValue = sitemapEntry.ChangeFrequency.ToString(); + } } - private async Task PopulateChangeFrequencyPriority(ContentTypesSitemapSource source, ContentItem contentItem, XElement url) + var priorityIntValue = sitemapMetadataAspect.Priority; + if (!priorityIntValue.HasValue) { - var sitemapMetadataAspect = await _contentManager.PopulateAspectAsync(contentItem); - - var changeFrequencyValue = sitemapMetadataAspect.ChangeFrequency; - if (string.IsNullOrEmpty(changeFrequencyValue)) + if (source.IndexAll) { - if (source.IndexAll) - { - changeFrequencyValue = source.ChangeFrequency.ToString(); - } - else if (source.LimitItems) - { - changeFrequencyValue = source.LimitedContentType.ChangeFrequency.ToString(); - } - else - { - var sitemapEntry = source.ContentTypes - .FirstOrDefault(ct => string.Equals(ct.ContentTypeName, contentItem.ContentType, StringComparison.Ordinal)); - - changeFrequencyValue = sitemapEntry.ChangeFrequency.ToString(); - } + priorityIntValue = source.Priority; } - - var priorityIntValue = sitemapMetadataAspect.Priority; - if (!priorityIntValue.HasValue) + else if (source.LimitItems) { - if (source.IndexAll) - { - priorityIntValue = source.Priority; - } - else if (source.LimitItems) - { - priorityIntValue = source.LimitedContentType.Priority; - } - else - { - var sitemapEntry = source.ContentTypes - .FirstOrDefault(ct => string.Equals(ct.ContentTypeName, contentItem.ContentType, StringComparison.Ordinal)); - - priorityIntValue = sitemapEntry.Priority; - } + priorityIntValue = source.LimitedContentType.Priority; } + else + { + var sitemapEntry = source.ContentTypes + .FirstOrDefault(ct => string.Equals(ct.ContentTypeName, contentItem.ContentType, StringComparison.Ordinal)); - var priorityValue = (priorityIntValue * 0.1f).Value.ToString(CultureInfo.InvariantCulture); + priorityIntValue = sitemapEntry.Priority; + } + } - var changeFreq = new XElement(_namespace + "changefreq"); - changeFreq.Add(changeFrequencyValue.ToLower()); - url.Add(changeFreq); + var priorityValue = (priorityIntValue * 0.1f).Value.ToString(CultureInfo.InvariantCulture); - var priority = new XElement(_namespace + "priority"); - priority.Add(priorityValue); - url.Add(priority); - } + var changeFreq = new XElement(_namespace + "changefreq"); + changeFreq.Add(changeFrequencyValue.ToLower()); + url.Add(changeFreq); - private static void PopulateLastMod(ContentItem contentItem, XElement url) + var priority = new XElement(_namespace + "priority"); + priority.Add(priorityValue); + url.Add(priority); + } + + private static void PopulateLastMod(ContentItem contentItem, XElement url) + { + // Last modified is not required. Do not include if content item has no modified date. + if (contentItem.ModifiedUtc.HasValue) { - // Last modified is not required. Do not include if content item has no modified date. - if (contentItem.ModifiedUtc.HasValue) - { - var lastMod = new XElement(_namespace + "lastmod"); - lastMod.Add(contentItem.ModifiedUtc.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); - url.Add(lastMod); - } + var lastMod = new XElement(_namespace + "lastmod"); + lastMod.Add(contentItem.ModifiedUtc.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); + url.Add(lastMod); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceDriver.cs index 291d9d3138d..118cc21f29d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceDriver.cs @@ -6,119 +6,118 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Contents.Sitemaps -{ - public sealed class ContentTypesSitemapSourceDriver : DisplayDriver - { - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; - - public ContentTypesSitemapSourceDriver(IRouteableContentTypeCoordinator routeableContentTypeCoordinator) - { - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; - } - - public override Task DisplayAsync(ContentTypesSitemapSource sitemapSource, BuildDisplayContext context) - { - return CombineAsync( - View("ContentTypesSitemapSource_SummaryAdmin", sitemapSource).Location("SummaryAdmin", "Content"), - View("ContentTypesSitemapSource_Thumbnail", sitemapSource).Location("Thumbnail", "Content") - ); - } +namespace OrchardCore.Contents.Sitemaps; - public override async Task EditAsync(ContentTypesSitemapSource sitemapSource, BuildEditorContext context) - { - var contentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); +public sealed class ContentTypesSitemapSourceDriver : DisplayDriver +{ + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; - var entries = contentTypeDefinitions - .Select(ctd => new ContentTypeSitemapEntryViewModel - { - ContentTypeName = ctd.Name, - ContentTypeDisplayName = ctd.DisplayName, - IsChecked = sitemapSource.ContentTypes.Any(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal)), - ChangeFrequency = sitemapSource.ContentTypes.FirstOrDefault(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal))?.ChangeFrequency ?? ChangeFrequency.Daily, - Priority = sitemapSource.ContentTypes.FirstOrDefault(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal))?.Priority ?? 5, - }) - .OrderBy(ctd => ctd.ContentTypeDisplayName) - .ToArray(); + public ContentTypesSitemapSourceDriver(IRouteableContentTypeCoordinator routeableContentTypeCoordinator) + { + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + } - var limitedEntries = contentTypeDefinitions - .Select(ctd => new ContentTypeLimitedSitemapEntryViewModel - { - ContentTypeName = ctd.Name, - ContentTypeDisplayName = ctd.DisplayName - }) - .OrderBy(ctd => ctd.ContentTypeDisplayName) - .ToArray(); + public override Task DisplayAsync(ContentTypesSitemapSource sitemapSource, BuildDisplayContext context) + { + return CombineAsync( + View("ContentTypesSitemapSource_SummaryAdmin", sitemapSource).Location("SummaryAdmin", "Content"), + View("ContentTypesSitemapSource_Thumbnail", sitemapSource).Location("Thumbnail", "Content") + ); + } - var limitedCtd = contentTypeDefinitions - .FirstOrDefault(ctd => string.Equals(sitemapSource.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); + public override async Task EditAsync(ContentTypesSitemapSource sitemapSource, BuildEditorContext context) + { + var contentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); - if (limitedCtd != null) + var entries = contentTypeDefinitions + .Select(ctd => new ContentTypeSitemapEntryViewModel { - var limitedEntry = limitedEntries.FirstOrDefault(le => string.Equals(le.ContentTypeName, limitedCtd.Name, StringComparison.Ordinal)); - limitedEntry.Priority = sitemapSource.LimitedContentType.Priority; - limitedEntry.ChangeFrequency = sitemapSource.LimitedContentType.ChangeFrequency; - limitedEntry.Skip = sitemapSource.LimitedContentType.Skip; - limitedEntry.Take = sitemapSource.LimitedContentType.Take; - } + ContentTypeName = ctd.Name, + ContentTypeDisplayName = ctd.DisplayName, + IsChecked = sitemapSource.ContentTypes.Any(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal)), + ChangeFrequency = sitemapSource.ContentTypes.FirstOrDefault(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal))?.ChangeFrequency ?? ChangeFrequency.Daily, + Priority = sitemapSource.ContentTypes.FirstOrDefault(s => string.Equals(s.ContentTypeName, ctd.Name, StringComparison.Ordinal))?.Priority ?? 5, + }) + .OrderBy(ctd => ctd.ContentTypeDisplayName) + .ToArray(); - return Initialize("ContentTypesSitemapSource_Edit", model => + var limitedEntries = contentTypeDefinitions + .Select(ctd => new ContentTypeLimitedSitemapEntryViewModel { - model.IndexAll = sitemapSource.IndexAll; - model.LimitItems = sitemapSource.LimitItems; - model.Priority = sitemapSource.Priority; - model.ChangeFrequency = sitemapSource.ChangeFrequency; - model.ContentTypes = entries; - model.LimitedContentTypes = limitedEntries; - model.LimitedContentType = limitedCtd != null ? limitedCtd.Name : contentTypeDefinitions.FirstOrDefault().Name; - model.SitemapSource = sitemapSource; - }).Location("Content"); + ContentTypeName = ctd.Name, + ContentTypeDisplayName = ctd.DisplayName + }) + .OrderBy(ctd => ctd.ContentTypeDisplayName) + .ToArray(); + + var limitedCtd = contentTypeDefinitions + .FirstOrDefault(ctd => string.Equals(sitemapSource.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); + + if (limitedCtd != null) + { + var limitedEntry = limitedEntries.FirstOrDefault(le => string.Equals(le.ContentTypeName, limitedCtd.Name, StringComparison.Ordinal)); + limitedEntry.Priority = sitemapSource.LimitedContentType.Priority; + limitedEntry.ChangeFrequency = sitemapSource.LimitedContentType.ChangeFrequency; + limitedEntry.Skip = sitemapSource.LimitedContentType.Skip; + limitedEntry.Take = sitemapSource.LimitedContentType.Take; } - public override async Task UpdateAsync(ContentTypesSitemapSource sitemap, UpdateEditorContext context) + return Initialize("ContentTypesSitemapSource_Edit", model => { - var model = new ContentTypesSitemapSourceViewModel(); + model.IndexAll = sitemapSource.IndexAll; + model.LimitItems = sitemapSource.LimitItems; + model.Priority = sitemapSource.Priority; + model.ChangeFrequency = sitemapSource.ChangeFrequency; + model.ContentTypes = entries; + model.LimitedContentTypes = limitedEntries; + model.LimitedContentType = limitedCtd != null ? limitedCtd.Name : contentTypeDefinitions.FirstOrDefault().Name; + model.SitemapSource = sitemapSource; + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, - Prefix, - m => m.IndexAll, - m => m.LimitItems, - m => m.Priority, - m => m.ChangeFrequency, - m => m.ContentTypes, - m => m.LimitedContentTypes, - m => m.LimitedContentType - ); + public override async Task UpdateAsync(ContentTypesSitemapSource sitemap, UpdateEditorContext context) + { + var model = new ContentTypesSitemapSourceViewModel(); - sitemap.IndexAll = model.IndexAll; - sitemap.LimitItems = model.LimitItems; - sitemap.Priority = model.Priority; - sitemap.ChangeFrequency = model.ChangeFrequency; - sitemap.ContentTypes = model.ContentTypes - .Where(x => x.IsChecked == true) - .Select(x => new ContentTypeSitemapEntry - { - ContentTypeName = x.ContentTypeName, - ChangeFrequency = x.ChangeFrequency, - Priority = x.Priority, - }) - .ToArray(); + await context.Updater.TryUpdateModelAsync(model, + Prefix, + m => m.IndexAll, + m => m.LimitItems, + m => m.Priority, + m => m.ChangeFrequency, + m => m.ContentTypes, + m => m.LimitedContentTypes, + m => m.LimitedContentType + ); - var limitedEntry = model.LimitedContentTypes.FirstOrDefault(lct => string.Equals(lct.ContentTypeName, model.LimitedContentType, StringComparison.Ordinal)); - if (limitedEntry != null) - { - sitemap.LimitedContentType.ContentTypeName = limitedEntry.ContentTypeName; - sitemap.LimitedContentType.ChangeFrequency = limitedEntry.ChangeFrequency; - sitemap.LimitedContentType.Priority = limitedEntry.Priority; - sitemap.LimitedContentType.Skip = limitedEntry.Skip; - sitemap.LimitedContentType.Take = limitedEntry.Take; - } - else + sitemap.IndexAll = model.IndexAll; + sitemap.LimitItems = model.LimitItems; + sitemap.Priority = model.Priority; + sitemap.ChangeFrequency = model.ChangeFrequency; + sitemap.ContentTypes = model.ContentTypes + .Where(x => x.IsChecked == true) + .Select(x => new ContentTypeSitemapEntry { - sitemap.LimitedContentType = new LimitedContentTypeSitemapEntry(); - } + ContentTypeName = x.ContentTypeName, + ChangeFrequency = x.ChangeFrequency, + Priority = x.Priority, + }) + .ToArray(); - return await EditAsync(sitemap, context); + var limitedEntry = model.LimitedContentTypes.FirstOrDefault(lct => string.Equals(lct.ContentTypeName, model.LimitedContentType, StringComparison.Ordinal)); + if (limitedEntry != null) + { + sitemap.LimitedContentType.ContentTypeName = limitedEntry.ContentTypeName; + sitemap.LimitedContentType.ChangeFrequency = limitedEntry.ChangeFrequency; + sitemap.LimitedContentType.Priority = limitedEntry.Priority; + sitemap.LimitedContentType.Skip = limitedEntry.Skip; + sitemap.LimitedContentType.Take = limitedEntry.Take; } + else + { + sitemap.LimitedContentType = new LimitedContentTypeSitemapEntry(); + } + + return await EditAsync(sitemap, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceModifiedDateProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceModifiedDateProvider.cs index 3e174d4689e..386a43b2283 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceModifiedDateProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceModifiedDateProvider.cs @@ -9,60 +9,59 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class ContentTypesSitemapSourceModifiedDateProvider : SitemapSourceModifiedDateProviderBase { - public class ContentTypesSitemapSourceModifiedDateProvider : SitemapSourceModifiedDateProviderBase + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + private readonly ISession _session; + + public ContentTypesSitemapSourceModifiedDateProvider( + IRouteableContentTypeCoordinator routeableContentTypeCoordinator, + ISession session + ) { - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; - private readonly ISession _session; + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + _session = session; + } - public ContentTypesSitemapSourceModifiedDateProvider( - IRouteableContentTypeCoordinator routeableContentTypeCoordinator, - ISession session - ) - { - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; - _session = session; - } + public override async Task GetLastModifiedDateAsync(ContentTypesSitemapSource source) + { + ContentItem lastModifiedContentItem; - public override async Task GetLastModifiedDateAsync(ContentTypesSitemapSource source) + if (source.IndexAll) { - ContentItem lastModifiedContentItem; - - if (source.IndexAll) - { - var typesToIndex = (await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync()) - .Select(ctd => ctd.Name); + var typesToIndex = (await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync()) + .Select(ctd => ctd.Name); - var query = _session.Query() - .With(x => x.Published && x.ContentType.IsIn(typesToIndex)) - .OrderByDescending(x => x.ModifiedUtc); + var query = _session.Query() + .With(x => x.Published && x.ContentType.IsIn(typesToIndex)) + .OrderByDescending(x => x.ModifiedUtc); - lastModifiedContentItem = await query.FirstOrDefaultAsync(); - } - else if (source.LimitItems) - { - var query = _session.Query() - .With(x => x.Published && x.ContentType == source.LimitedContentType.ContentTypeName) - .OrderByDescending(x => x.ModifiedUtc); - - lastModifiedContentItem = await query.FirstOrDefaultAsync(); - } - else - { - var typesToIndex = (await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync()) - .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) - .Select(ctd => ctd.Name); + lastModifiedContentItem = await query.FirstOrDefaultAsync(); + } + else if (source.LimitItems) + { + var query = _session.Query() + .With(x => x.Published && x.ContentType == source.LimitedContentType.ContentTypeName) + .OrderByDescending(x => x.ModifiedUtc); - // This is an estimate, so doesn't take into account Take/Skip values. - var query = _session.Query() - .With(x => x.Published && x.ContentType.IsIn(typesToIndex)) - .OrderByDescending(x => x.ModifiedUtc); + lastModifiedContentItem = await query.FirstOrDefaultAsync(); + } + else + { + var typesToIndex = (await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync()) + .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) + .Select(ctd => ctd.Name); - lastModifiedContentItem = await query.FirstOrDefaultAsync(); - } + // This is an estimate, so doesn't take into account Take/Skip values. + var query = _session.Query() + .With(x => x.Published && x.ContentType.IsIn(typesToIndex)) + .OrderByDescending(x => x.ModifiedUtc); - return lastModifiedContentItem.ModifiedUtc; + lastModifiedContentItem = await query.FirstOrDefaultAsync(); } + + return lastModifiedContentItem.ModifiedUtc; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceUpdateHandler.cs index 488f2399df4..a7ce02c0912 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceUpdateHandler.cs @@ -6,66 +6,65 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class ContentTypesSitemapSourceUpdateHandler : ISitemapSourceUpdateHandler { - public class ContentTypesSitemapSourceUpdateHandler : ISitemapSourceUpdateHandler + private readonly ISitemapManager _sitemapManager; + + public ContentTypesSitemapSourceUpdateHandler(ISitemapManager sitemapManager) { - private readonly ISitemapManager _sitemapManager; + _sitemapManager = sitemapManager; + } - public ContentTypesSitemapSourceUpdateHandler(ISitemapManager sitemapManager) - { - _sitemapManager = sitemapManager; - } + public async Task UpdateSitemapAsync(SitemapUpdateContext context) + { + var contentItem = context.UpdateObject as ContentItem; - public async Task UpdateSitemapAsync(SitemapUpdateContext context) + if (contentItem == null) { - var contentItem = context.UpdateObject as ContentItem; - - if (contentItem == null) - { - return; - } + return; + } - var sitemaps = (await _sitemapManager.LoadSitemapsAsync()) - .Where(s => s.GetType() == typeof(Sitemap)); + var sitemaps = (await _sitemapManager.LoadSitemapsAsync()) + .Where(s => s.GetType() == typeof(Sitemap)); - if (!sitemaps.Any()) - { - return; - } + if (!sitemaps.Any()) + { + return; + } - var contentTypeName = contentItem.ContentType; + var contentTypeName = contentItem.ContentType; - foreach (var sitemap in sitemaps) + foreach (var sitemap in sitemaps) + { + // Do not break out of this loop, as it must check each sitemap. + foreach (var source in sitemap.SitemapSources + .Select(s => s as ContentTypesSitemapSource)) { - // Do not break out of this loop, as it must check each sitemap. - foreach (var source in sitemap.SitemapSources - .Select(s => s as ContentTypesSitemapSource)) + if (source == null) { - if (source == null) - { - continue; - } + continue; + } - if (source.IndexAll) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } - else if (source.LimitItems && string.Equals(source.LimitedContentType.ContentTypeName, contentTypeName, StringComparison.Ordinal)) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } - else if (source.ContentTypes.Any(ct => string.Equals(ct.ContentTypeName, contentTypeName, StringComparison.Ordinal))) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } + if (source.IndexAll) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; + } + else if (source.LimitItems && string.Equals(source.LimitedContentType.ContentTypeName, contentTypeName, StringComparison.Ordinal)) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; + } + else if (source.ContentTypes.Any(ct => string.Equals(ct.ContentTypeName, contentTypeName, StringComparison.Ordinal))) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; } } - - await _sitemapManager.UpdateSitemapAsync(); } + + await _sitemapManager.UpdateSitemapAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceViewModel.cs index 395ebbff736..abda8c6327c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapSourceViewModel.cs @@ -1,44 +1,43 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class ContentTypesSitemapSourceViewModel { - public class ContentTypesSitemapSourceViewModel - { - public bool IndexAll { get; set; } + public bool IndexAll { get; set; } - public bool LimitItems { get; set; } + public bool LimitItems { get; set; } - public ChangeFrequency ChangeFrequency { get; set; } + public ChangeFrequency ChangeFrequency { get; set; } - public int Priority { get; set; } = 5; + public int Priority { get; set; } = 5; - public ContentTypeSitemapEntryViewModel[] ContentTypes { get; set; } = []; + public ContentTypeSitemapEntryViewModel[] ContentTypes { get; set; } = []; - public ContentTypeLimitedSitemapEntryViewModel[] LimitedContentTypes { get; set; } = []; + public ContentTypeLimitedSitemapEntryViewModel[] LimitedContentTypes { get; set; } = []; - public string LimitedContentType { get; set; } + public string LimitedContentType { get; set; } - [BindNever] - public SitemapSource SitemapSource { get; set; } - } + [BindNever] + public SitemapSource SitemapSource { get; set; } +} - public class ContentTypeSitemapEntryViewModel - { - public bool IsChecked { get; set; } - public string ContentTypeDisplayName { get; set; } - public string ContentTypeName { get; set; } - public ChangeFrequency ChangeFrequency { get; set; } - public int Priority { get; set; } - } +public class ContentTypeSitemapEntryViewModel +{ + public bool IsChecked { get; set; } + public string ContentTypeDisplayName { get; set; } + public string ContentTypeName { get; set; } + public ChangeFrequency ChangeFrequency { get; set; } + public int Priority { get; set; } +} - public class ContentTypeLimitedSitemapEntryViewModel - { - public string ContentTypeDisplayName { get; set; } - public string ContentTypeName { get; set; } - public ChangeFrequency ChangeFrequency { get; set; } = ChangeFrequency.Daily; - public int Priority { get; set; } = 5; - public int Skip { get; set; } - public int Take { get; set; } = 50_000; - } +public class ContentTypeLimitedSitemapEntryViewModel +{ + public string ContentTypeDisplayName { get; set; } + public string ContentTypeName { get; set; } + public ChangeFrequency ChangeFrequency { get; set; } = ChangeFrequency.Daily; + public int Priority { get; set; } = 5; + public int Skip { get; set; } + public int Take { get; set; } = 50_000; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs index 5c6a4b20118..a864cc2aef7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/ContentTypesSitemapUpdateHandler.cs @@ -2,29 +2,28 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.Sitemaps.Handlers; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class ContentTypesSitemapUpdateHandler : ContentHandlerBase { - public class ContentTypesSitemapUpdateHandler : ContentHandlerBase - { - private readonly ISitemapUpdateHandler _sitemapUpdateHandler; + private readonly ISitemapUpdateHandler _sitemapUpdateHandler; - public ContentTypesSitemapUpdateHandler(ISitemapUpdateHandler sitemapUpdateHandler) - { - _sitemapUpdateHandler = sitemapUpdateHandler; - } + public ContentTypesSitemapUpdateHandler(ISitemapUpdateHandler sitemapUpdateHandler) + { + _sitemapUpdateHandler = sitemapUpdateHandler; + } - public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); + public override Task PublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); - public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); + public override Task UnpublishedAsync(PublishContentContext context) => UpdateSitemapAsync(context); - private Task UpdateSitemapAsync(ContentContextBase context) + private Task UpdateSitemapAsync(ContentContextBase context) + { + var updateContext = new SitemapUpdateContext { - var updateContext = new SitemapUpdateContext - { - UpdateObject = context.ContentItem, - }; + UpdateObject = context.ContentItem, + }; - return _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); - } + return _sitemapUpdateHandler.UpdateSitemapAsync(updateContext); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/DefaultContentItemsQueryProvider.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/DefaultContentItemsQueryProvider.cs index 26b2d7ce850..799318d487e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/DefaultContentItemsQueryProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Sitemaps/DefaultContentItemsQueryProvider.cs @@ -9,69 +9,68 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Contents.Sitemaps +namespace OrchardCore.Contents.Sitemaps; + +public class DefaultContentItemsQueryProvider : IContentItemsQueryProvider { - public class DefaultContentItemsQueryProvider : IContentItemsQueryProvider + private readonly ISession _session; + private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + + public DefaultContentItemsQueryProvider( + ISession session, + IRouteableContentTypeCoordinator routeableContentTypeCoordinator + ) + { + _session = session; + _routeableContentTypeCoordinator = routeableContentTypeCoordinator; + } + + public async Task GetContentItemsAsync(ContentTypesSitemapSource source, ContentItemsQueryContext context) { - private readonly ISession _session; - private readonly IRouteableContentTypeCoordinator _routeableContentTypeCoordinator; + var routeableContentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); - public DefaultContentItemsQueryProvider( - ISession session, - IRouteableContentTypeCoordinator routeableContentTypeCoordinator - ) + if (source.IndexAll) { - _session = session; - _routeableContentTypeCoordinator = routeableContentTypeCoordinator; - } + var rctdNames = routeableContentTypeDefinitions.Select(rctd => rctd.Name); - public async Task GetContentItemsAsync(ContentTypesSitemapSource source, ContentItemsQueryContext context) + var queryResults = await _session.Query() + .With(x => x.Published && x.ContentType.IsIn(rctdNames)) + .OrderBy(x => x.CreatedUtc) + .ListAsync(); + + context.ContentItems = queryResults; + } + else if (source.LimitItems) { - var routeableContentTypeDefinitions = await _routeableContentTypeCoordinator.ListRoutableTypeDefinitionsAsync(); + // Test that content type is still valid to include in sitemap. + var typeIsValid = routeableContentTypeDefinitions + .Any(ctd => string.Equals(source.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); - if (source.IndexAll) + if (typeIsValid) { - var rctdNames = routeableContentTypeDefinitions.Select(rctd => rctd.Name); - var queryResults = await _session.Query() - .With(x => x.Published && x.ContentType.IsIn(rctdNames)) + .With(x => x.ContentType == source.LimitedContentType.ContentTypeName && x.Published) .OrderBy(x => x.CreatedUtc) + .Skip(source.LimitedContentType.Skip) + .Take(source.LimitedContentType.Take) .ListAsync(); context.ContentItems = queryResults; } - else if (source.LimitItems) - { - // Test that content type is still valid to include in sitemap. - var typeIsValid = routeableContentTypeDefinitions - .Any(ctd => string.Equals(source.LimitedContentType.ContentTypeName, ctd.Name, StringComparison.Ordinal)); - - if (typeIsValid) - { - var queryResults = await _session.Query() - .With(x => x.ContentType == source.LimitedContentType.ContentTypeName && x.Published) - .OrderBy(x => x.CreatedUtc) - .Skip(source.LimitedContentType.Skip) - .Take(source.LimitedContentType.Take) - .ListAsync(); - - context.ContentItems = queryResults; - } - } - else - { - // Test that content types are still valid to include in sitemap. - var typesToIndex = routeableContentTypeDefinitions - .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) - .Select(x => x.Name); + } + else + { + // Test that content types are still valid to include in sitemap. + var typesToIndex = routeableContentTypeDefinitions + .Where(ctd => source.ContentTypes.Any(s => string.Equals(ctd.Name, s.ContentTypeName, StringComparison.Ordinal))) + .Select(x => x.Name); - var queryResults = await _session.Query() - .With(x => x.ContentType.IsIn(typesToIndex) && x.Published) - .OrderBy(x => x.CreatedUtc) - .ListAsync(); + var queryResults = await _session.Query() + .With(x => x.ContentType.IsIn(typesToIndex) && x.Published) + .OrderBy(x => x.CreatedUtc) + .ListAsync(); - context.ContentItems = queryResults; - } + context.ContentItems = queryResults; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Startup.cs index 2870c72a596..10b95d342e3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Startup.cs @@ -55,244 +55,243 @@ using OrchardCore.Sitemaps.Services; using YesSql.Filters.Query; -namespace OrchardCore.Contents +namespace OrchardCore.Contents; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.AddSingleton(); + + services.Configure(o => { - services.AddSingleton(); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("contentitem", parser.ArgumentsListParser, ContentItemTag.WriteToAsync)); + }); - services.Configure(o => + services.Configure(o => + { + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register>(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + + o.Filters.AddFilter("display_text", DisplayTextFilter.DisplayText); + + o.Scope.SetValue("Content", new ObjectValue(new LiquidContentAccessor())); + o.MemberAccessStrategy.Register("ContentItemId", (obj, context) => { - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("contentitem", parser.ArgumentsListParser, ContentItemTag.WriteToAsync)); - }); + var liquidTemplateContext = (LiquidTemplateContext)context; - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register>(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - - o.Filters.AddFilter("display_text", DisplayTextFilter.DisplayText); - - o.Scope.SetValue("Content", new ObjectValue(new LiquidContentAccessor())); - o.MemberAccessStrategy.Register("ContentItemId", (obj, context) => + return new LiquidPropertyAccessor(liquidTemplateContext, async (contentItemId, context) => { - var liquidTemplateContext = (LiquidTemplateContext)context; - - return new LiquidPropertyAccessor(liquidTemplateContext, async (contentItemId, context) => - { - var contentManager = context.Services.GetRequiredService(); + var contentManager = context.Services.GetRequiredService(); - return FluidValue.Create(await contentManager.GetAsync(contentItemId), context.Options); - }); + return FluidValue.Create(await contentManager.GetAsync(contentItemId), context.Options); }); + }); - o.MemberAccessStrategy.Register("ContentItemVersionId", (obj, context) => - { - var liquidTemplateContext = (LiquidTemplateContext)context; - - return new LiquidPropertyAccessor(liquidTemplateContext, async (contentItemVersionId, context) => - { - var contentManager = context.Services.GetRequiredService(); - - return FluidValue.Create(await contentManager.GetVersionAsync(contentItemVersionId), context.Options); - }); - }); + o.MemberAccessStrategy.Register("ContentItemVersionId", (obj, context) => + { + var liquidTemplateContext = (LiquidTemplateContext)context; - o.MemberAccessStrategy.Register("Latest", (obj, context) => + return new LiquidPropertyAccessor(liquidTemplateContext, async (contentItemVersionId, context) => { - var liquidTemplateContext = (LiquidTemplateContext)context; + var contentManager = context.Services.GetRequiredService(); - return new LiquidPropertyAccessor(liquidTemplateContext, (name, context) => - { - return GetContentByHandleAsync(context, name, true); - }); + return FluidValue.Create(await contentManager.GetVersionAsync(contentItemVersionId), context.Options); }); + }); - o.MemberAccessStrategy.Register((obj, name, context) => GetContentByHandleAsync((LiquidTemplateContext)context, name)); + o.MemberAccessStrategy.Register("Latest", (obj, context) => + { + var liquidTemplateContext = (LiquidTemplateContext)context; - static async Task GetContentByHandleAsync(LiquidTemplateContext context, string handle, bool latest = false) + return new LiquidPropertyAccessor(liquidTemplateContext, (name, context) => { - var contentHandleManager = context.Services.GetRequiredService(); + return GetContentByHandleAsync(context, name, true); + }); + }); - var contentItemId = await contentHandleManager.GetContentItemIdAsync(handle); + o.MemberAccessStrategy.Register((obj, name, context) => GetContentByHandleAsync((LiquidTemplateContext)context, name)); - if (contentItemId == null) - { - return NilValue.Instance; - } + static async Task GetContentByHandleAsync(LiquidTemplateContext context, string handle, bool latest = false) + { + var contentHandleManager = context.Services.GetRequiredService(); - var contentManager = context.Services.GetRequiredService(); + var contentItemId = await contentHandleManager.GetContentItemIdAsync(handle); - var contentItem = await contentManager.GetAsync(contentItemId, latest ? VersionOptions.Latest : VersionOptions.Published); - return FluidValue.Create(contentItem, context.Options); + if (contentItemId == null) + { + return NilValue.Instance; } - }) - .AddLiquidFilter("display_url") - .AddLiquidFilter("shape_build_display") - .AddLiquidFilter("content_item_id") - .AddLiquidFilter("full_text"); - - services.AddContentManagement(); - services.AddContentManagementDisplay(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddRecipeExecutionStep(); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - services.AddDataMigration(); - - // Common Part - services.AddContentPart() - .UseDisplayDriver() - .UseDisplayDriver(); - - services.AddScoped(); - - // FullTextAspect - services.AddScoped(); - services.AddScoped(); - - services.AddTagHelpers(); - services.AddTagHelpers(); - services.Configure(options => + + var contentManager = context.Services.GetRequiredService(); + + var contentItem = await contentManager.GetAsync(contentItemId, latest ? VersionOptions.Latest : VersionOptions.Published); + return FluidValue.Create(contentItem, context.Options); + } + }) + .AddLiquidFilter("display_url") + .AddLiquidFilter("shape_build_display") + .AddLiquidFilter("content_item_id") + .AddLiquidFilter("full_text"); + + services.AddContentManagement(); + services.AddContentManagementDisplay(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddRecipeExecutionStep(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddDataMigration(); + + // Common Part + services.AddContentPart() + .UseDisplayDriver() + .UseDisplayDriver(); + + services.AddScoped(); + + // FullTextAspect + services.AddScoped(); + services.AddScoped(); + + services.AddTagHelpers(); + services.AddTagHelpers(); + services.Configure(options => + { + if (options.GlobalRouteValues.Count == 0) { - if (options.GlobalRouteValues.Count == 0) + options.GlobalRouteValues = new RouteValueDictionary { - options.GlobalRouteValues = new RouteValueDictionary - { - {"Area", "OrchardCore.Contents"}, - {"Controller", "Item"}, - {"Action", "Display"} - }; - - options.ContentItemIdKey = "contentItemId"; - options.ContainedContentItemIdKey = "containedContentItemId"; - options.JsonPathKey = "jsonPath"; - } - }); + {"Area", "OrchardCore.Contents"}, + {"Controller", "Item"}, + {"Action", "Display"} + }; + + options.ContentItemIdKey = "contentItemId"; + options.ContainedContentItemIdKey = "containedContentItemId"; + options.JsonPathKey = "jsonPath"; + } + }); - services.AddScoped(); + services.AddScoped(); - services.AddScoped, ContentOptionsDisplayDriver>(); + services.AddScoped, ContentOptionsDisplayDriver>(); - services.AddScoped(typeof(IContentItemRecursionHelper<>), typeof(ContentItemRecursionHelper<>)); + services.AddScoped(typeof(IContentItemRecursionHelper<>), typeof(ContentItemRecursionHelper<>)); - services.AddSingleton(sp => + services.AddSingleton(sp => + { + var filterProviders = sp.GetServices(); + var builder = new QueryEngineBuilder(); + foreach (var provider in filterProviders) { - var filterProviders = sp.GetServices(); - var builder = new QueryEngineBuilder(); - foreach (var provider in filterProviders) - { - provider.Build(builder); - } + provider.Build(builder); + } - var parser = builder.Build(); + var parser = builder.Build(); - return new DefaultContentsAdminListFilterParser(parser); - }); + return new DefaultContentsAdminListFilterParser(parser); + }); - services.AddTransient(); - } + services.AddTransient(); + } - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.AddGetContentEndpoint() - .AddCreateContentEndpoint() - .AddDeleteContentEndpoint(); - - var itemControllerName = typeof(ItemController).ControllerName(); - - routes.MapAreaControllerRoute( - name: "DisplayContentItem", - areaName: "OrchardCore.Contents", - pattern: "Contents/ContentItems/{contentItemId}", - defaults: new { controller = itemControllerName, action = nameof(ItemController.Display) } - ); - - routes.MapAreaControllerRoute( - name: "PreviewContentItem", - areaName: "OrchardCore.Contents", - pattern: "Contents/ContentItems/{contentItemId}/Preview", - defaults: new { controller = itemControllerName, action = nameof(ItemController.Preview) } - ); - - routes.MapAreaControllerRoute( - name: "PreviewContentItemVersion", - areaName: "OrchardCore.Contents", - pattern: "Contents/ContentItems/{contentItemId}/Version/{version}/Preview", - defaults: new { controller = itemControllerName, action = nameof(ItemController.Preview) } - ); - } + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.AddGetContentEndpoint() + .AddCreateContentEndpoint() + .AddDeleteContentEndpoint(); + + var itemControllerName = typeof(ItemController).ControllerName(); + + routes.MapAreaControllerRoute( + name: "DisplayContentItem", + areaName: "OrchardCore.Contents", + pattern: "Contents/ContentItems/{contentItemId}", + defaults: new { controller = itemControllerName, action = nameof(ItemController.Display) } + ); + + routes.MapAreaControllerRoute( + name: "PreviewContentItem", + areaName: "OrchardCore.Contents", + pattern: "Contents/ContentItems/{contentItemId}/Preview", + defaults: new { controller = itemControllerName, action = nameof(ItemController.Preview) } + ); + + routes.MapAreaControllerRoute( + name: "PreviewContentItemVersion", + areaName: "OrchardCore.Contents", + pattern: "Contents/ContentItems/{contentItemId}/Version/{version}/Preview", + defaults: new { controller = itemControllerName, action = nameof(ItemController.Preview) } + ); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddDeployment(); - services.AddSiteSettingsPropertyDeploymentStep(S => S["Content Audit Trail settings"], S => S["Exports the content audit trail settings."]); - } + services.AddDeployment(); + services.AddDeployment(); + services.AddSiteSettingsPropertyDeploymentStep(S => S["Content Audit Trail settings"], S => S["Exports the content audit trail settings."]); } +} - [RequireFeatures("OrchardCore.AdminMenu")] - public sealed class AdminMenuStartup : StartupBase +[RequireFeatures("OrchardCore.AdminMenu")] +public sealed class AdminMenuStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddAdminNode(); - } + services.AddAdminNode(); } +} - [Feature("OrchardCore.Contents.FileContentDefinition")] - public sealed class FileContentDefinitionStartup : StartupBase +[Feature("OrchardCore.Contents.FileContentDefinition")] +public sealed class FileContentDefinitionStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddFileContentDefinitionStore(); - } + services.AddFileContentDefinitionStore(); } +} - [RequireFeatures("OrchardCore.Sitemaps")] - public sealed class SitemapsStartup : StartupBase +[RequireFeatures("OrchardCore.Sitemaps")] +public sealed class SitemapsStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, ContentTypesSitemapSourceDriver>(); - services.AddScoped>(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, ContentTypesSitemapSourceDriver>(); + services.AddScoped>(); + services.AddScoped(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Feeds")] - public sealed class FeedsStartup : StartupBase +[RequireFeatures("OrchardCore.Feeds")] +public sealed class FeedsStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Feeds - services.AddScoped(); - } + // Feeds + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/DisplayContentItemViewComponent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/DisplayContentItemViewComponent.cs index d2559ecd39a..2e4c4a50a63 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/DisplayContentItemViewComponent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/DisplayContentItemViewComponent.cs @@ -5,41 +5,40 @@ using OrchardCore.ContentManagement.Display; using OrchardCore.DisplayManagement.ModelBinding; -namespace OrchardCore.Contents.ViewComponents +namespace OrchardCore.Contents.ViewComponents; + +public class DisplayContentItemViewComponent : ViewComponent { - public class DisplayContentItemViewComponent : ViewComponent + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IUpdateModelAccessor _modelUpdaterAccessor; + + public DisplayContentItemViewComponent( + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + IUpdateModelAccessor modelUpdaterAccessor) + { + _contentItemDisplayManager = contentItemDisplayManager; + _contentManager = contentManager; + _modelUpdaterAccessor = modelUpdaterAccessor; + } + + public async Task InvokeAsync(string contentItemId = null, string displayType = null) { - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IUpdateModelAccessor _modelUpdaterAccessor; - - public DisplayContentItemViewComponent( - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - IUpdateModelAccessor modelUpdaterAccessor) + ContentItem contentItem = null; + + if (contentItemId != null) { - _contentItemDisplayManager = contentItemDisplayManager; - _contentManager = contentManager; - _modelUpdaterAccessor = modelUpdaterAccessor; + contentItem = await _contentManager.GetAsync(contentItemId); } - public async Task InvokeAsync(string contentItemId = null, string displayType = null) + if (contentItem == null) { - ContentItem contentItem = null; - - if (contentItemId != null) - { - contentItem = await _contentManager.GetAsync(contentItemId); - } - - if (contentItem == null) - { - throw new ArgumentException("Content item not found"); - } + throw new ArgumentException("Content item not found"); + } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _modelUpdaterAccessor.ModelUpdater, displayType); + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _modelUpdaterAccessor.ModelUpdater, displayType); - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/SelectContentTypesViewComponent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/SelectContentTypesViewComponent.cs index 026715d0f4d..2235801bb29 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/SelectContentTypesViewComponent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewComponents/SelectContentTypesViewComponent.cs @@ -7,37 +7,36 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Contents.ViewModels; -namespace OrchardCore.Contents.ViewComponents +namespace OrchardCore.Contents.ViewComponents; + +public class SelectContentTypesViewComponent : ViewComponent { - public class SelectContentTypesViewComponent : ViewComponent + private readonly IContentDefinitionManager _contentDefinitionManager; + + public SelectContentTypesViewComponent(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public async Task InvokeAsync(IEnumerable selectedContentTypes, string htmlName, string stereotype) { - private readonly IContentDefinitionManager _contentDefinitionManager; + selectedContentTypes ??= Array.Empty(); - public SelectContentTypesViewComponent(IContentDefinitionManager contentDefinitionManager) + var contentTypes = await ContentTypeSelection.BuildAsync(_contentDefinitionManager, selectedContentTypes); + + if (!string.IsNullOrEmpty(stereotype)) { - _contentDefinitionManager = contentDefinitionManager; + contentTypes = contentTypes + .Where(x => x.ContentTypeDefinition.GetStereotype() == stereotype) + .ToArray(); } - public async Task InvokeAsync(IEnumerable selectedContentTypes, string htmlName, string stereotype) + var model = new SelectContentTypesViewModel { - selectedContentTypes ??= Array.Empty(); - - var contentTypes = await ContentTypeSelection.BuildAsync(_contentDefinitionManager, selectedContentTypes); - - if (!string.IsNullOrEmpty(stereotype)) - { - contentTypes = contentTypes - .Where(x => x.ContentTypeDefinition.GetStereotype() == stereotype) - .ToArray(); - } + HtmlName = htmlName, + ContentTypeSelections = contentTypes + }; - var model = new SelectContentTypesViewModel - { - HtmlName = htmlName, - ContentTypeSelections = contentTypes - }; - - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/AllContentDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/AllContentDeploymentStepViewModel.cs index 7cd08061a6a..0c988edac96 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/AllContentDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/AllContentDeploymentStepViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class AllContentDeploymentStepViewModel { - public class AllContentDeploymentStepViewModel - { - public bool ExportAsSetupRecipe { get; set; } - } + public bool ExportAsSetupRecipe { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/CommonPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/CommonPartSettingsViewModel.cs index 0e1b04a16e5..f8e4c8a9220 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/CommonPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/CommonPartSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class CommonPartSettingsViewModel { - public class CommonPartSettingsViewModel - { - public bool DisplayDateEditor { get; set; } - public bool DisplayOwnerEditor { get; set; } - } + public bool DisplayDateEditor { get; set; } + public bool DisplayOwnerEditor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ContentDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ContentDeploymentStepViewModel.cs index e75f4436be5..9ac59aa85b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ContentDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ContentDeploymentStepViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class ContentDeploymentStepViewModel { - public class ContentDeploymentStepViewModel - { - public string[] ContentTypes { get; set; } - public bool ExportAsSetupRecipe { get; set; } - } + public string[] ContentTypes { get; set; } + public bool ExportAsSetupRecipe { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/DateEditorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/DateEditorViewModel.cs index d6080a8fdf5..718272445db 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/DateEditorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/DateEditorViewModel.cs @@ -1,9 +1,8 @@ using System; -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class DateEditorViewModel { - public class DateEditorViewModel - { - public DateTime? LocalDateTime { get; set; } - } + public DateTime? LocalDateTime { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/FullTextAspectSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/FullTextAspectSettingsViewModel.cs index f5e32290d3a..97199112be6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/FullTextAspectSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/FullTextAspectSettingsViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class FullTextAspectSettingsViewModel { - public class FullTextAspectSettingsViewModel - { - public bool IncludeFullTextTemplate { get; set; } - public string FullTextTemplate { get; set; } - public bool IncludeBodyAspect { get; set; } - public bool IncludeDisplayText { get; set; } - } + public bool IncludeFullTextTemplate { get; set; } + public string FullTextTemplate { get; set; } + public bool IncludeBodyAspect { get; set; } + public bool IncludeDisplayText { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/IndexingEditorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/IndexingEditorViewModel.cs index 5e4cc05b5d7..05bb138d746 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/IndexingEditorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/IndexingEditorViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class IndexingEditorViewModel { - public class IndexingEditorViewModel - { - public bool IsIndexed { get; set; } - } + public bool IsIndexed { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentTypesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentTypesViewModel.cs index 1431bd8b1b9..91e8adc764b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentTypesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentTypesViewModel.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class ListContentTypesViewModel { - public class ListContentTypesViewModel - { - public IEnumerable Types { get; set; } - } + public IEnumerable Types { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentsViewModel.cs index a99e00a8cea..9dea3968edc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/ListContentsViewModel.cs @@ -1,19 +1,18 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class ListContentsViewModel { - public class ListContentsViewModel - { - public ContentOptionsViewModel Options { get; set; } + public ContentOptionsViewModel Options { get; set; } - [BindNever] - public dynamic Header { get; set; } + [BindNever] + public dynamic Header { get; set; } - [BindNever] - public List ContentItems { get; set; } + [BindNever] + public List ContentItems { get; set; } - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/OwnerEditorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/OwnerEditorViewModel.cs index c1b43915d33..9ec691f9e03 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/OwnerEditorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/OwnerEditorViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class OwnerEditorViewModel { - public class OwnerEditorViewModel - { - public string OwnerName { get; set; } - } + public string OwnerName { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/SelectContentTypesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/SelectContentTypesViewModel.cs index a05e7f86349..bcb144511c3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/SelectContentTypesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/ViewModels/SelectContentTypesViewModel.cs @@ -5,32 +5,31 @@ using OrchardCore.ContentManagement.Metadata; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.Contents.ViewModels +namespace OrchardCore.Contents.ViewModels; + +public class SelectContentTypesViewModel { - public class SelectContentTypesViewModel - { - public string HtmlName { get; set; } - public ContentTypeSelection[] ContentTypeSelections { get; set; } - } + public string HtmlName { get; set; } + public ContentTypeSelection[] ContentTypeSelections { get; set; } +} - public class ContentTypeSelection - { - public bool IsSelected { get; set; } - public ContentTypeDefinition ContentTypeDefinition { get; set; } +public class ContentTypeSelection +{ + public bool IsSelected { get; set; } + public ContentTypeDefinition ContentTypeDefinition { get; set; } - public static async Task BuildAsync(IContentDefinitionManager contentDefinitionManager, IEnumerable selectedContentTypes) - { - var contentTypes = (await contentDefinitionManager.ListTypeDefinitionsAsync()) - .Select(x => - new ContentTypeSelection - { - IsSelected = selectedContentTypes.Contains(x.Name), - ContentTypeDefinition = x - }) - .OrderBy(type => type.ContentTypeDefinition.DisplayName) - .ToArray(); + public static async Task BuildAsync(IContentDefinitionManager contentDefinitionManager, IEnumerable selectedContentTypes) + { + var contentTypes = (await contentDefinitionManager.ListTypeDefinitionsAsync()) + .Select(x => + new ContentTypeSelection + { + IsSelected = selectedContentTypes.Contains(x.Name), + ContentTypeDefinition = x + }) + .OrderBy(type => type.ContentTypeDefinition.DisplayName) + .ToArray(); - return contentTypes; - } + return contentTypes; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentActivity.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentActivity.cs index ecf1874f896..d18375f006c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentActivity.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentActivity.cs @@ -12,190 +12,189 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public abstract class ContentActivity : Activity { - public abstract class ContentActivity : Activity - { - protected readonly IStringLocalizer S; + protected readonly IStringLocalizer S; - protected ContentActivity( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - { - ContentManager = contentManager; - ScriptEvaluator = scriptEvaluator; - S = localizer; - } + protected ContentActivity( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + { + ContentManager = contentManager; + ScriptEvaluator = scriptEvaluator; + S = localizer; + } - protected IContentManager ContentManager { get; } + protected IContentManager ContentManager { get; } - protected IWorkflowScriptEvaluator ScriptEvaluator { get; } + protected IWorkflowScriptEvaluator ScriptEvaluator { get; } - /// - /// A updated when executed inline from a . - /// - protected ContentEventContext InlineEvent { get; private set; } = new ContentEventContext(); + /// + /// A updated when executed inline from a . + /// + protected ContentEventContext InlineEvent { get; private set; } = new ContentEventContext(); - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - /// - /// An expression that evaluates to an item. - /// - public WorkflowExpression Content - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + /// + /// An expression that evaluates to an item. + /// + public WorkflowExpression Content + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"]); - } + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"]); + } + + public override Task OnInputReceivedAsync(WorkflowExecutionContext workflowContext, IDictionary input) + { + var contentEvent = input?.GetValue(ContentEventConstants.ContentEventInputKey); - public override Task OnInputReceivedAsync(WorkflowExecutionContext workflowContext, IDictionary input) + if (contentEvent != null) { - var contentEvent = input?.GetValue(ContentEventConstants.ContentEventInputKey); + InlineEvent = contentEvent; - if (contentEvent != null) - { - InlineEvent = contentEvent; + InlineEvent.IsStart = workflowContext.Status == WorkflowStatus.Starting; + } - InlineEvent.IsStart = workflowContext.Status == WorkflowStatus.Starting; - } + return Task.CompletedTask; + } - return Task.CompletedTask; - } + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes("Done"); + } - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes("Done"); - } + public override async Task OnWorkflowRestartingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken = default) + { + ContentItem contentItem = null; - public override async Task OnWorkflowRestartingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken = default) + if (workflowContext.Input.TryGetValue(ContentEventConstants.ContentEventInputKey, out var contentEvent)) { - ContentItem contentItem = null; - - if (workflowContext.Input.TryGetValue(ContentEventConstants.ContentEventInputKey, out var contentEvent)) + if (contentEvent is not JsonObject jsonObject) { - if (contentEvent is not JsonObject jsonObject) + jsonObject = []; + if (contentEvent is Dictionary items) { - jsonObject = []; - if (contentEvent is Dictionary items) + foreach (var item in items) { - foreach (var item in items) - { - jsonObject[item.Key] = JsonSerializer.SerializeToNode(item.Value); - } + jsonObject[item.Key] = JsonSerializer.SerializeToNode(item.Value); } } + } - var contentEventContext = jsonObject.ToObject(); + var contentEventContext = jsonObject.ToObject(); - if (contentEventContext?.ContentItemVersionId != null) - { - contentItem = await ContentManager.GetVersionAsync(contentEventContext.ContentItemVersionId); - } - if (contentItem == null && contentEventContext?.ContentItemId != null) - { - contentItem = await ContentManager.GetAsync(contentEventContext.ContentItemId); - } + if (contentEventContext?.ContentItemVersionId != null) + { + contentItem = await ContentManager.GetVersionAsync(contentEventContext.ContentItemVersionId); + } + if (contentItem == null && contentEventContext?.ContentItemId != null) + { + contentItem = await ContentManager.GetAsync(contentEventContext.ContentItemId); } + } - if (contentItem == null && workflowContext.Input.TryGetValue(ContentEventConstants.ContentItemInputKey, out var contentItemEvent)) + if (contentItem == null && workflowContext.Input.TryGetValue(ContentEventConstants.ContentItemInputKey, out var contentItemEvent)) + { + if (contentItemEvent is not JsonObject jsonObject) { - if (contentItemEvent is not JsonObject jsonObject) + jsonObject = []; + if (contentEvent is Dictionary items) { - jsonObject = []; - if (contentEvent is Dictionary items) + foreach (var item in items) { - foreach (var item in items) - { - jsonObject[item.Key] = JsonSerializer.SerializeToNode(item.Value); - } + jsonObject[item.Key] = JsonSerializer.SerializeToNode(item.Value); } } - - var existingContentItem = jsonObject.ToObject(); - - if (existingContentItem?.ContentItemId != null) - { - contentItem = await ContentManager.GetAsync(existingContentItem.ContentItemId); - } } - if (contentItem != null) + var existingContentItem = jsonObject.ToObject(); + + if (existingContentItem?.ContentItemId != null) { - workflowContext.Input[ContentEventConstants.ContentItemInputKey] = contentItem; + contentItem = await ContentManager.GetAsync(existingContentItem.ContentItemId); } } - protected virtual async Task GetContentAsync(WorkflowExecutionContext workflowContext) + if (contentItem != null) { - IContent content; + workflowContext.Input[ContentEventConstants.ContentItemInputKey] = contentItem; + } + } - // Try to evaluate a content item from the Content expression, if provided. - if (!string.IsNullOrWhiteSpace(Content.Expression)) - { - var expression = new WorkflowExpression { Expression = Content.Expression }; - var result = await ScriptEvaluator.EvaluateAsync(expression, workflowContext); + protected virtual async Task GetContentAsync(WorkflowExecutionContext workflowContext) + { + IContent content; - if (result is ContentItem contentItem) - { - content = contentItem; - } - else if (result is string contentItemId) - { - content = new ContentItemIdExpressionResult(contentItemId); - } - else - { - // Try to map the result to a content item. - var json = JConvert.SerializeObject(result); - content = JConvert.DeserializeObject(json); - } + // Try to evaluate a content item from the Content expression, if provided. + if (!string.IsNullOrWhiteSpace(Content.Expression)) + { + var expression = new WorkflowExpression { Expression = Content.Expression }; + var result = await ScriptEvaluator.EvaluateAsync(expression, workflowContext); + + if (result is ContentItem contentItem) + { + content = contentItem; } - else + else if (result is string contentItemId) { - // If no expression was provided, see if the content item was provided as an input or as a property. - content = workflowContext.Input.GetValue(ContentEventConstants.ContentItemInputKey) - ?? workflowContext.Properties.GetValue(ContentEventConstants.ContentItemInputKey); + content = new ContentItemIdExpressionResult(contentItemId); } - - if (content?.ContentItem?.ContentItemId != null) + else { - return content; + // Try to map the result to a content item. + var json = JConvert.SerializeObject(result); + content = JConvert.DeserializeObject(json); } - - return null; } - - protected virtual async Task GetContentItemIdAsync(WorkflowExecutionContext workflowContext) + else { - // Try to evaluate a content item id from the Content expression, if provided. - if (!string.IsNullOrWhiteSpace(Content.Expression)) - { - var expression = new WorkflowExpression { Expression = Content.Expression }; - var contentItemIdResult = await ScriptEvaluator.EvaluateAsync(expression, workflowContext); - - if (contentItemIdResult is string contentItemId) - { - return contentItemId; - } - } + // If no expression was provided, see if the content item was provided as an input or as a property. + content = workflowContext.Input.GetValue(ContentEventConstants.ContentItemInputKey) + ?? workflowContext.Properties.GetValue(ContentEventConstants.ContentItemInputKey); + } - return null; + if (content?.ContentItem?.ContentItemId != null) + { + return content; } - protected class ContentItemIdExpressionResult : IContent + return null; + } + + protected virtual async Task GetContentItemIdAsync(WorkflowExecutionContext workflowContext) + { + // Try to evaluate a content item id from the Content expression, if provided. + if (!string.IsNullOrWhiteSpace(Content.Expression)) { - public ContentItemIdExpressionResult(string contentItemId) + var expression = new WorkflowExpression { Expression = Content.Expression }; + var contentItemIdResult = await ScriptEvaluator.EvaluateAsync(expression, workflowContext); + + if (contentItemIdResult is string contentItemId) { - ContentItem = new ContentItem() { ContentItemId = contentItemId }; + return contentItemId; } + } - public ContentItem ContentItem { get; } + return null; + } + + protected class ContentItemIdExpressionResult : IContent + { + public ContentItemIdExpressionResult(string contentItemId) + { + ContentItem = new ContentItem() { ContentItemId = contentItemId }; } + + public ContentItem ContentItem { get; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentCreatedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentCreatedEvent.cs index 2df0fd8d65e..2d851168ac0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentCreatedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentCreatedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentCreatedEvent : ContentEvent { - public class ContentCreatedEvent : ContentEvent + public ContentCreatedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentCreatedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentCreatedEvent); + public override string Name => nameof(ContentCreatedEvent); - public override LocalizedString DisplayText => S["Content Created Event"]; - } + public override LocalizedString DisplayText => S["Content Created Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDeletedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDeletedEvent.cs index 4b4ea669d4e..dbe7da9e002 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDeletedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDeletedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentDeletedEvent : ContentEvent { - public class ContentDeletedEvent : ContentEvent + public ContentDeletedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentDeletedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentDeletedEvent); + public override string Name => nameof(ContentDeletedEvent); - public override LocalizedString DisplayText => S["Content Deleted Event"]; - } + public override LocalizedString DisplayText => S["Content Deleted Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDraftSavedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDraftSavedEvent.cs index 96d69de978b..c41948aade8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDraftSavedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentDraftSavedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentDraftSavedEvent : ContentEvent { - public class ContentDraftSavedEvent : ContentEvent + public ContentDraftSavedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentDraftSavedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentDraftSavedEvent); + public override string Name => nameof(ContentDraftSavedEvent); - public override LocalizedString DisplayText => S["Content Save Draft Event"]; - } + public override LocalizedString DisplayText => S["Content Save Draft Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentEvent.cs index 7e0efd08885..0feb7c81ab3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentEvent.cs @@ -7,47 +7,46 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public abstract class ContentEvent : ContentActivity, IEvent { - public abstract class ContentEvent : ContentActivity, IEvent + protected ContentEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - protected ContentEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public IList ContentTypeFilter - { - get => GetProperty>(defaultValue: () => []); - set => SetProperty(value); - } + public IList ContentTypeFilter + { + get => GetProperty>(defaultValue: () => []); + set => SetProperty(value); + } - public override async Task CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var content = await GetContentAsync(workflowContext); + public override async Task CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var content = await GetContentAsync(workflowContext); - if (content == null) - { - return false; - } + if (content == null) + { + return false; + } - var contentTypes = ContentTypeFilter.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); + var contentTypes = ContentTypeFilter.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); - // "" means 'any'. - return contentTypes.Count == 0 || contentTypes.Any(contentType => content.ContentItem.ContentType == contentType); - } + // "" means 'any'. + return contentTypes.Count == 0 || contentTypes.Any(contentType => content.ContentItem.ContentType == contentType); + } - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Halt(); - } + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Halt(); + } - public override ActivityExecutionResult Resume(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes("Done"); - } + public override ActivityExecutionResult Resume(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes("Done"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentPublishedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentPublishedEvent.cs index 470f663f340..16287076d4e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentPublishedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentPublishedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentPublishedEvent : ContentEvent { - public class ContentPublishedEvent : ContentEvent + public ContentPublishedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentPublishedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentPublishedEvent); + public override string Name => nameof(ContentPublishedEvent); - public override LocalizedString DisplayText => S["Content Published Event"]; - } + public override LocalizedString DisplayText => S["Content Published Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentTask.cs index cebe217c0a3..63f6ad9f800 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentTask.cs @@ -3,16 +3,15 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public abstract class ContentTask : ContentActivity, ITask { - public abstract class ContentTask : ContentActivity, ITask + protected ContentTask( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - protected ContentTask( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUnpublishedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUnpublishedEvent.cs index 49da36b13b6..610512a8c84 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUnpublishedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUnpublishedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentUnpublishedEvent : ContentEvent { - public class ContentUnpublishedEvent : ContentEvent + public ContentUnpublishedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentUnpublishedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentUnpublishedEvent); + public override string Name => nameof(ContentUnpublishedEvent); - public override LocalizedString DisplayText => S["Content Unpublished Event"]; - } + public override LocalizedString DisplayText => S["Content Unpublished Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUpdatedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUpdatedEvent.cs index 57cfa6446bc..fa778288d1a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUpdatedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentUpdatedEvent.cs @@ -2,20 +2,19 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentUpdatedEvent : ContentEvent { - public class ContentUpdatedEvent : ContentEvent + public ContentUpdatedEvent( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentUpdatedEvent( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentUpdatedEvent); + public override string Name => nameof(ContentUpdatedEvent); - public override LocalizedString DisplayText => S["Content Updated Event"]; - } + public override LocalizedString DisplayText => S["Content Updated Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentVersionedEvent.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentVersionedEvent.cs index c7364c7319f..c2b692e2c8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentVersionedEvent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/ContentVersionedEvent.cs @@ -2,19 +2,18 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class ContentVersionedEvent : ContentEvent { - public class ContentVersionedEvent : ContentEvent + public ContentVersionedEvent(IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public ContentVersionedEvent(IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(ContentVersionedEvent); + public override string Name => nameof(ContentVersionedEvent); - public override LocalizedString DisplayText => S["Content Versioned Event"]; - } + public override LocalizedString DisplayText => S["Content Versioned Event"]; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/CreateContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/CreateContentTask.cs index 3f37c035222..d98cc89b8c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/CreateContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/CreateContentTask.cs @@ -12,119 +12,118 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class CreateContentTask : ContentTask { - public class CreateContentTask : ContentTask + private readonly IWorkflowExpressionEvaluator _expressionEvaluator; + private readonly JavaScriptEncoder _javaScriptEncoder; + + public CreateContentTask( + IContentManager contentManager, + IWorkflowExpressionEvaluator expressionEvaluator, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer, + JavaScriptEncoder javaScriptEncoder) + : base(contentManager, scriptEvaluator, localizer) { - private readonly IWorkflowExpressionEvaluator _expressionEvaluator; - private readonly JavaScriptEncoder _javaScriptEncoder; - - public CreateContentTask( - IContentManager contentManager, - IWorkflowExpressionEvaluator expressionEvaluator, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer, - JavaScriptEncoder javaScriptEncoder) - : base(contentManager, scriptEvaluator, localizer) - { - _expressionEvaluator = expressionEvaluator; - _javaScriptEncoder = javaScriptEncoder; - } + _expressionEvaluator = expressionEvaluator; + _javaScriptEncoder = javaScriptEncoder; + } - public override string Name => nameof(CreateContentTask); + public override string Name => nameof(CreateContentTask); - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - public override LocalizedString DisplayText => S["Create Content Task"]; + public override LocalizedString DisplayText => S["Create Content Task"]; - public string ContentType - { - get => GetProperty(); - set => SetProperty(value); - } + public string ContentType + { + get => GetProperty(); + set => SetProperty(value); + } - public bool Publish - { - get => GetProperty(); - set => SetProperty(value); - } + public bool Publish + { + get => GetProperty(); + set => SetProperty(value); + } - public WorkflowExpression ContentProperties - { - get => GetProperty(() => new WorkflowExpression(JConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }, JOptions.Indented))); - set => SetProperty(value); - } + public WorkflowExpression ContentProperties + { + get => GetProperty(() => new WorkflowExpression(JConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }, JOptions.Indented))); + set => SetProperty(value); + } - public override bool CanExecute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override bool CanExecute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return !string.IsNullOrEmpty(ContentType); + } + + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Failed"]); + } + + public async override Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + if (InlineEvent.IsStart && InlineEvent.ContentType == ContentType) { - return !string.IsNullOrEmpty(ContentType); + if (InlineEvent.Name == nameof(ContentUpdatedEvent)) + { + throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentUpdatedEvent)}' of the same content type, which would result in an infinitive loop."); + } + + if (InlineEvent.Name == nameof(ContentCreatedEvent)) + { + throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't create the content item as it is executed inline from a starting '{nameof(ContentCreatedEvent)}' of the same content type, which would result in an infinitive loop."); + } + + if (Publish && InlineEvent.Name == nameof(ContentPublishedEvent)) + { + throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); + } + + if (!Publish && InlineEvent.Name == nameof(ContentDraftSavedEvent)) + { + throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't create the content item as it is executed inline from a starting '{nameof(ContentDraftSavedEvent)}' of the same content type, which would result in an infinitive loop."); + } } - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + var contentItem = await ContentManager.NewAsync(ContentType); + + if (!string.IsNullOrWhiteSpace(ContentProperties.Expression)) { - return Outcomes(S["Done"], S["Failed"]); + var contentProperties = await _expressionEvaluator.EvaluateAsync(ContentProperties, workflowContext, _javaScriptEncoder); + contentItem.Merge(JObject.Parse(contentProperties)); } - public async override Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + var result = await ContentManager.UpdateValidateAndCreateAsync(contentItem, VersionOptions.Draft); + + if (result.Succeeded) { - if (InlineEvent.IsStart && InlineEvent.ContentType == ContentType) + if (Publish) { - if (InlineEvent.Name == nameof(ContentUpdatedEvent)) - { - throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentUpdatedEvent)}' of the same content type, which would result in an infinitive loop."); - } - - if (InlineEvent.Name == nameof(ContentCreatedEvent)) - { - throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't create the content item as it is executed inline from a starting '{nameof(ContentCreatedEvent)}' of the same content type, which would result in an infinitive loop."); - } - - if (Publish && InlineEvent.Name == nameof(ContentPublishedEvent)) - { - throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); - } - - if (!Publish && InlineEvent.Name == nameof(ContentDraftSavedEvent)) - { - throw new InvalidOperationException($"The '{nameof(CreateContentTask)}' can't create the content item as it is executed inline from a starting '{nameof(ContentDraftSavedEvent)}' of the same content type, which would result in an infinitive loop."); - } + await ContentManager.PublishAsync(contentItem); } - - var contentItem = await ContentManager.NewAsync(ContentType); - - if (!string.IsNullOrWhiteSpace(ContentProperties.Expression)) + else { - var contentProperties = await _expressionEvaluator.EvaluateAsync(ContentProperties, workflowContext, _javaScriptEncoder); - contentItem.Merge(JObject.Parse(contentProperties)); + await ContentManager.SaveDraftAsync(contentItem); } - var result = await ContentManager.UpdateValidateAndCreateAsync(contentItem, VersionOptions.Draft); - - if (result.Succeeded) + if (string.IsNullOrEmpty(workflowContext.CorrelationId)) { - if (Publish) - { - await ContentManager.PublishAsync(contentItem); - } - else - { - await ContentManager.SaveDraftAsync(contentItem); - } - - if (string.IsNullOrEmpty(workflowContext.CorrelationId)) - { - workflowContext.CorrelationId = contentItem.ContentItemId; - } - - workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; - workflowContext.LastResult = contentItem; - - return Outcomes("Done"); + workflowContext.CorrelationId = contentItem.ContentItemId; } - workflowContext.LastResult = result; + workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; + workflowContext.LastResult = contentItem; - return Outcomes("Failed"); + return Outcomes("Done"); } + + workflowContext.LastResult = result; + + return Outcomes("Failed"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/DeleteContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/DeleteContentTask.cs index 5381862eae4..71405de6fa2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/DeleteContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/DeleteContentTask.cs @@ -8,58 +8,57 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class DeleteContentTask : ContentTask { - public class DeleteContentTask : ContentTask + public DeleteContentTask(IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public DeleteContentTask(IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(DeleteContentTask); + public override string Name => nameof(DeleteContentTask); - public override LocalizedString DisplayText => S["Delete Content Task"]; + public override LocalizedString DisplayText => S["Delete Content Task"]; - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Deleted"], S["Noop"]); + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var content = (await GetContentAsync(workflowContext)) + ?? throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' failed to retrieve the content item."); + + if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) { - return Outcomes(S["Deleted"], S["Noop"]); + return Outcomes("Noop"); } - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var content = (await GetContentAsync(workflowContext)) - ?? throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' failed to retrieve the content item."); + var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Latest); - if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) + if (contentItem == null) + { + if (content is ContentItemIdExpressionResult) { - return Outcomes("Noop"); + throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' failed to retrieve the content item."); } - var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Latest); - - if (contentItem == null) - { - if (content is ContentItemIdExpressionResult) - { - throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' failed to retrieve the content item."); - } - - contentItem = content.ContentItem; - } + contentItem = content.ContentItem; + } - if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentDeletedEvent)) - { - throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' can't delete the content item as it is executed inline from a starting '{nameof(ContentDeletedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentDeletedEvent)) + { + throw new InvalidOperationException($"The '{nameof(DeleteContentTask)}' can't delete the content item as it is executed inline from a starting '{nameof(ContentDeletedEvent)}' of the same content type, which would result in an infinitive loop."); + } - await ContentManager.RemoveAsync(contentItem); + await ContentManager.RemoveAsync(contentItem); - return Outcomes("Deleted"); - } + return Outcomes("Deleted"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/PublishContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/PublishContentTask.cs index 2875b2fd763..6e7db199a46 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/PublishContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/PublishContentTask.cs @@ -8,59 +8,58 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class PublishContentTask : ContentTask { - public class PublishContentTask : ContentTask + public PublishContentTask( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public PublishContentTask( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(PublishContentTask); + public override string Name => nameof(PublishContentTask); - public override LocalizedString DisplayText => S["Publish Content Task"]; + public override LocalizedString DisplayText => S["Publish Content Task"]; - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Published"], S["Noop"]); + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var content = (await GetContentAsync(workflowContext)) + ?? throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' failed to retrieve the content item."); + + if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) { - return Outcomes(S["Published"], S["Noop"]); + return Outcomes("Noop"); } - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var content = (await GetContentAsync(workflowContext)) - ?? throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' failed to retrieve the content item."); + var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.DraftRequired); - if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) + if (contentItem == null) + { + if (content is ContentItemIdExpressionResult) { - return Outcomes("Noop"); + throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' failed to retrieve the content item."); } - var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.DraftRequired); - - if (contentItem == null) - { - if (content is ContentItemIdExpressionResult) - { - throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' failed to retrieve the content item."); - } - - contentItem = content.ContentItem; - } + contentItem = content.ContentItem; + } - if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentPublishedEvent)) - { - throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentPublishedEvent)) + { + throw new InvalidOperationException($"The '{nameof(PublishContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); + } - await ContentManager.PublishAsync(contentItem); + await ContentManager.PublishAsync(contentItem); - return Outcomes("Published"); - } + return Outcomes("Published"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/RetrieveContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/RetrieveContentTask.cs index 1f0e09893e1..de7506c6789 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/RetrieveContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/RetrieveContentTask.cs @@ -9,46 +9,45 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class RetrieveContentTask : ContentTask { - public class RetrieveContentTask : ContentTask + public RetrieveContentTask( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public RetrieveContentTask( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } - - public override string Name => nameof(RetrieveContentTask); + } - public override LocalizedString DisplayText => S["Retrieve Content Task"]; + public override string Name => nameof(RetrieveContentTask); - public override LocalizedString Category => S["Content"]; + public override LocalizedString DisplayText => S["Retrieve Content Task"]; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Retrieved"]); - } + public override LocalizedString Category => S["Content"]; - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var contentItemId = (await GetContentItemIdAsync(workflowContext)) - ?? throw new InvalidOperationException($"The '{nameof(RetrieveContentTask)}' failed to evaluate the 'ContentItemId'."); + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Retrieved"]); + } - var contentItem = (await ContentManager.GetAsync(contentItemId, VersionOptions.Latest)) - ?? throw new InvalidOperationException($"The '{nameof(RetrieveContentTask)}' failed to retrieve the content item."); + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var contentItemId = (await GetContentItemIdAsync(workflowContext)) + ?? throw new InvalidOperationException($"The '{nameof(RetrieveContentTask)}' failed to evaluate the 'ContentItemId'."); - if (string.IsNullOrEmpty(workflowContext.CorrelationId)) - { - workflowContext.CorrelationId = contentItem.ContentItemId; - } - - workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; - workflowContext.LastResult = contentItem; + var contentItem = (await ContentManager.GetAsync(contentItemId, VersionOptions.Latest)) + ?? throw new InvalidOperationException($"The '{nameof(RetrieveContentTask)}' failed to retrieve the content item."); - return Outcomes("Retrieved"); + if (string.IsNullOrEmpty(workflowContext.CorrelationId)) + { + workflowContext.CorrelationId = contentItem.ContentItemId; } + + workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; + workflowContext.LastResult = contentItem; + + return Outcomes("Retrieved"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UnpublishContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UnpublishContentTask.cs index a74c535cb18..4c22e657d23 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UnpublishContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UnpublishContentTask.cs @@ -8,59 +8,58 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class UnpublishContentTask : ContentTask { - public class UnpublishContentTask : ContentTask + public UnpublishContentTask( + IContentManager contentManager, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer) + : base(contentManager, scriptEvaluator, localizer) { - public UnpublishContentTask( - IContentManager contentManager, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer) - : base(contentManager, scriptEvaluator, localizer) - { - } + } - public override string Name => nameof(UnpublishContentTask); + public override string Name => nameof(UnpublishContentTask); - public override LocalizedString DisplayText => S["Unpublish Content Task"]; + public override LocalizedString DisplayText => S["Unpublish Content Task"]; - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Unpublished"], S["Noop"]); + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var content = (await GetContentAsync(workflowContext)) + ?? throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' failed to retrieve the content item."); + + if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) { - return Outcomes(S["Unpublished"], S["Noop"]); + return Outcomes("Noop"); } - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var content = (await GetContentAsync(workflowContext)) - ?? throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' failed to retrieve the content item."); + var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Latest); - if (string.Equals(InlineEvent.ContentItemId, content.ContentItem.ContentItemId, StringComparison.OrdinalIgnoreCase)) + if (contentItem == null) + { + if (content is ContentItemIdExpressionResult) { - return Outcomes("Noop"); + throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' failed to retrieve the content item."); } - var contentItem = await ContentManager.GetAsync(content.ContentItem.ContentItemId, VersionOptions.Latest); - - if (contentItem == null) - { - if (content is ContentItemIdExpressionResult) - { - throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' failed to retrieve the content item."); - } - - contentItem = content.ContentItem; - } + contentItem = content.ContentItem; + } - if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentUnpublishedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' can't unpublish the content item as it is executed inline from a starting '{nameof(ContentUnpublishedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType && InlineEvent.Name == nameof(ContentUnpublishedEvent)) + { + throw new InvalidOperationException($"The '{nameof(UnpublishContentTask)}' can't unpublish the content item as it is executed inline from a starting '{nameof(ContentUnpublishedEvent)}' of the same content type, which would result in an infinitive loop."); + } - await ContentManager.UnpublishAsync(contentItem); + await ContentManager.UnpublishAsync(contentItem); - return Outcomes("Unpublished"); - } + return Outcomes("Unpublished"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UpdateContentTask.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UpdateContentTask.cs index 8190b52a9d8..13292c1fdaf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UpdateContentTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Activities/UpdateContentTask.cs @@ -15,163 +15,162 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Activities +namespace OrchardCore.Contents.Workflows.Activities; + +public class UpdateContentTask : ContentTask { - public class UpdateContentTask : ContentTask + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IWorkflowExpressionEvaluator _expressionEvaluator; + private readonly JavaScriptEncoder _javaScriptEncoder; + + public UpdateContentTask( + IContentManager contentManager, + IUpdateModelAccessor updateModelAccessor, + IWorkflowExpressionEvaluator expressionEvaluator, + IWorkflowScriptEvaluator scriptEvaluator, + IStringLocalizer localizer, + JavaScriptEncoder javaScriptEncoder) + : base(contentManager, scriptEvaluator, localizer) { - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IWorkflowExpressionEvaluator _expressionEvaluator; - private readonly JavaScriptEncoder _javaScriptEncoder; - - public UpdateContentTask( - IContentManager contentManager, - IUpdateModelAccessor updateModelAccessor, - IWorkflowExpressionEvaluator expressionEvaluator, - IWorkflowScriptEvaluator scriptEvaluator, - IStringLocalizer localizer, - JavaScriptEncoder javaScriptEncoder) - : base(contentManager, scriptEvaluator, localizer) - { - _updateModelAccessor = updateModelAccessor; - _expressionEvaluator = expressionEvaluator; - _javaScriptEncoder = javaScriptEncoder; - } + _updateModelAccessor = updateModelAccessor; + _expressionEvaluator = expressionEvaluator; + _javaScriptEncoder = javaScriptEncoder; + } - public override string Name => nameof(UpdateContentTask); + public override string Name => nameof(UpdateContentTask); - public override LocalizedString Category => S["Content"]; + public override LocalizedString Category => S["Content"]; - public override LocalizedString DisplayText => S["Update Content Task"]; + public override LocalizedString DisplayText => S["Update Content Task"]; - public bool Publish - { - get => GetProperty(); - set => SetProperty(value); - } + public bool Publish + { + get => GetProperty(); + set => SetProperty(value); + } - public WorkflowExpression ContentItemIdExpression - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression ContentItemIdExpression + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression ContentProperties - { - get => GetProperty(() => - // new WorkflowExpression(JsonConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }, Formatting.Indented))); - new WorkflowExpression(JConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }))); + public WorkflowExpression ContentProperties + { + get => GetProperty(() => + // new WorkflowExpression(JsonConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }, Formatting.Indented))); + new WorkflowExpression(JConvert.SerializeObject(new { DisplayText = S["Enter a title"].Value }))); - set => SetProperty(value); - } + set => SetProperty(value); + } - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"], S["Failed"]); - } + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Failed"]); + } - public async override Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var contentItemId = (await GetContentItemIdAsync(workflowContext)) - ?? throw new InvalidOperationException($"The {nameof(UpdateContentTask)} failed to evaluate the 'ContentItemId'."); + public async override Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var contentItemId = (await GetContentItemIdAsync(workflowContext)) + ?? throw new InvalidOperationException($"The {nameof(UpdateContentTask)} failed to evaluate the 'ContentItemId'."); - var inlineEventOfSameContentItemId = string.Equals(InlineEvent.ContentItemId, contentItemId, StringComparison.OrdinalIgnoreCase); + var inlineEventOfSameContentItemId = string.Equals(InlineEvent.ContentItemId, contentItemId, StringComparison.OrdinalIgnoreCase); - if (inlineEventOfSameContentItemId) + if (inlineEventOfSameContentItemId) + { + if (InlineEvent.Name == nameof(ContentPublishedEvent)) { - if (InlineEvent.Name == nameof(ContentPublishedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a '{nameof(ContentPublishedEvent)}' of the same content item, please use an event that is triggered earlier."); - } - - if (InlineEvent.Name == nameof(ContentDraftSavedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a '{nameof(ContentDraftSavedEvent)}' of the same content item, please use an event that is triggered earlier."); - } + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a '{nameof(ContentPublishedEvent)}' of the same content item, please use an event that is triggered earlier."); } - ContentItem contentItem = null; - - if (!inlineEventOfSameContentItemId) + if (InlineEvent.Name == nameof(ContentDraftSavedEvent)) { - contentItem = await ContentManager.GetAsync(contentItemId, VersionOptions.DraftRequired); - } - else - { - contentItem = workflowContext.Input.GetValue(ContentEventConstants.ContentItemInputKey)?.ContentItem; + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a '{nameof(ContentDraftSavedEvent)}' of the same content item, please use an event that is triggered earlier."); } + } - if (contentItem == null) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' failed to retrieve the content item."); - } + ContentItem contentItem = null; - if (!inlineEventOfSameContentItemId && InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType) - { - if (InlineEvent.Name == nameof(ContentUpdatedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentUpdatedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (!inlineEventOfSameContentItemId) + { + contentItem = await ContentManager.GetAsync(contentItemId, VersionOptions.DraftRequired); + } + else + { + contentItem = workflowContext.Input.GetValue(ContentEventConstants.ContentItemInputKey)?.ContentItem; + } - if (Publish && InlineEvent.Name == nameof(ContentPublishedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (contentItem == null) + { + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' failed to retrieve the content item."); + } - if (!Publish && InlineEvent.Name == nameof(ContentDraftSavedEvent)) - { - throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentDraftSavedEvent)}' of the same content type, which would result in an infinitive loop."); - } + if (!inlineEventOfSameContentItemId && InlineEvent.IsStart && InlineEvent.ContentType == contentItem.ContentType) + { + if (InlineEvent.Name == nameof(ContentUpdatedEvent)) + { + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentUpdatedEvent)}' of the same content type, which would result in an infinitive loop."); } - if (!string.IsNullOrWhiteSpace(ContentProperties.Expression)) + if (Publish && InlineEvent.Name == nameof(ContentPublishedEvent)) { - var contentProperties = await _expressionEvaluator.EvaluateAsync(ContentProperties, workflowContext, _javaScriptEncoder); - contentItem.Merge(JsonNode.Parse(contentProperties), new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace }); + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't publish the content item as it is executed inline from a starting '{nameof(ContentPublishedEvent)}' of the same content type, which would result in an infinitive loop."); } - if (!inlineEventOfSameContentItemId) + if (!Publish && InlineEvent.Name == nameof(ContentDraftSavedEvent)) { - await ContentManager.UpdateAsync(contentItem); + throw new InvalidOperationException($"The '{nameof(UpdateContentTask)}' can't update the content item as it is executed inline from a starting '{nameof(ContentDraftSavedEvent)}' of the same content type, which would result in an infinitive loop."); } + } - var result = await ContentManager.ValidateAsync(contentItem); + if (!string.IsNullOrWhiteSpace(ContentProperties.Expression)) + { + var contentProperties = await _expressionEvaluator.EvaluateAsync(ContentProperties, workflowContext, _javaScriptEncoder); + contentItem.Merge(JsonNode.Parse(contentProperties), new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Replace }); + } - if (result.Succeeded) + if (!inlineEventOfSameContentItemId) + { + await ContentManager.UpdateAsync(contentItem); + } + + var result = await ContentManager.ValidateAsync(contentItem); + + if (result.Succeeded) + { + if (!inlineEventOfSameContentItemId) { - if (!inlineEventOfSameContentItemId) + if (Publish) { - if (Publish) - { - await ContentManager.PublishAsync(contentItem); - } - else - { - await ContentManager.SaveDraftAsync(contentItem); - } + await ContentManager.PublishAsync(contentItem); } - - if (string.IsNullOrEmpty(workflowContext.CorrelationId)) + else { - workflowContext.CorrelationId = contentItem.ContentItemId; + await ContentManager.SaveDraftAsync(contentItem); } - - workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; - workflowContext.LastResult = contentItem; - - return Outcomes("Done"); } - if (inlineEventOfSameContentItemId) + if (string.IsNullOrEmpty(workflowContext.CorrelationId)) { - _updateModelAccessor.ModelUpdater.ModelState.AddModelError(nameof(UpdateContentTask), - $"The '{workflowContext.WorkflowType.Name}:{nameof(UpdateContentTask)}' failed to update the content item: " - + string.Join(", ", result.Errors)); + workflowContext.CorrelationId = contentItem.ContentItemId; } - workflowContext.LastResult = result; + workflowContext.Properties[ContentEventConstants.ContentItemInputKey] = contentItem; + workflowContext.LastResult = contentItem; + + return Outcomes("Done"); + } - return Outcomes("Failed"); + if (inlineEventOfSameContentItemId) + { + _updateModelAccessor.ModelUpdater.ModelState.AddModelError(nameof(UpdateContentTask), + $"The '{workflowContext.WorkflowType.Name}:{nameof(UpdateContentTask)}' failed to update the content item: " + + string.Join(", ", result.Errors)); } + + workflowContext.LastResult = result; + + return Outcomes("Failed"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentCreatedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentCreatedEventDisplayDriver.cs index 933f658ed90..1b9108d8555 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentCreatedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentCreatedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentCreatedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentCreatedEventDisplayDriver : ContentEventDisplayDriver + public ContentCreatedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentCreatedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDeletedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDeletedEventDisplayDriver.cs index 933b9e96e8d..a64f73759bf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDeletedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDeletedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentDeletedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentDeletedEventDisplayDriver : ContentEventDisplayDriver + public ContentDeletedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentDeletedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDraftSavedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDraftSavedEventDisplayDriver.cs index 5075484ddb5..ea5a9995ee7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDraftSavedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentDraftSavedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentDraftSavedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentDraftSavedEventDisplayDriver : ContentEventDisplayDriver + public ContentDraftSavedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentDraftSavedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentEventDisplayDriver.cs index 4a66d78a9be..7e1eac82432 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentEventDisplayDriver.cs @@ -8,57 +8,56 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Workflows.Display; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public abstract class ContentEventDisplayDriver : ActivityDisplayDriver where TActivity : ContentEvent where TViewModel : ContentEventViewModel, new() { - public abstract class ContentEventDisplayDriver : ActivityDisplayDriver where TActivity : ContentEvent where TViewModel : ContentEventViewModel, new() + protected ContentEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) { - protected ContentEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - { - ContentDefinitionManager = contentDefinitionManager; - } + ContentDefinitionManager = contentDefinitionManager; + } - protected IContentDefinitionManager ContentDefinitionManager { get; } + protected IContentDefinitionManager ContentDefinitionManager { get; } - protected override void EditActivity(TActivity source, TViewModel target) - { - target.SelectedContentTypeNames = source.ContentTypeFilter; - } + protected override void EditActivity(TActivity source, TViewModel target) + { + target.SelectedContentTypeNames = source.ContentTypeFilter; + } - public override async Task UpdateAsync(TActivity model, UpdateEditorContext context) - { - var viewModel = new TViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, x => x.SelectedContentTypeNames); + public override async Task UpdateAsync(TActivity model, UpdateEditorContext context) + { + var viewModel = new TViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, x => x.SelectedContentTypeNames); - model.ContentTypeFilter = (await FilterContentTypesQueryAsync(viewModel.SelectedContentTypeNames)).ToArray(); + model.ContentTypeFilter = (await FilterContentTypesQueryAsync(viewModel.SelectedContentTypeNames)).ToArray(); - return await EditAsync(model, context); - } + return await EditAsync(model, context); + } - public override Task DisplayAsync(TActivity activity, BuildDisplayContext context) - { - return CombineAsync( - Shape($"{typeof(TActivity).Name}_Fields_Thumbnail", new ContentEventViewModel(activity)).Location("Thumbnail", "Content"), - Factory($"{typeof(TActivity).Name}_Fields_Design", async ctx => + public override Task DisplayAsync(TActivity activity, BuildDisplayContext context) + { + return CombineAsync( + Shape($"{typeof(TActivity).Name}_Fields_Thumbnail", new ContentEventViewModel(activity)).Location("Thumbnail", "Content"), + Factory($"{typeof(TActivity).Name}_Fields_Design", async ctx => + { + var contentTypeDefinitions = (await ContentDefinitionManager.ListTypeDefinitionsAsync()).ToDictionary(x => x.Name); + var selectedContentTypeDefinitions = activity.ContentTypeFilter.Select(x => contentTypeDefinitions[x]).ToList(); + + var shape = new ContentEventViewModel { - var contentTypeDefinitions = (await ContentDefinitionManager.ListTypeDefinitionsAsync()).ToDictionary(x => x.Name); - var selectedContentTypeDefinitions = activity.ContentTypeFilter.Select(x => contentTypeDefinitions[x]).ToList(); - - var shape = new ContentEventViewModel - { - ContentTypeFilter = selectedContentTypeDefinitions, - Activity = activity, - }; + ContentTypeFilter = selectedContentTypeDefinitions, + Activity = activity, + }; - return shape; - }).Location("Design", "Content") - ); - } + return shape; + }).Location("Design", "Content") + ); + } - protected async Task> FilterContentTypesQueryAsync(IEnumerable contentTypeNames) - { - var contentTypeDefinitions = (await ContentDefinitionManager.ListTypeDefinitionsAsync()).ToDictionary(x => x.Name); + protected async Task> FilterContentTypesQueryAsync(IEnumerable contentTypeNames) + { + var contentTypeDefinitions = (await ContentDefinitionManager.ListTypeDefinitionsAsync()).ToDictionary(x => x.Name); - return contentTypeNames.Where(x => !string.IsNullOrWhiteSpace(x) && contentTypeDefinitions.ContainsKey(x)); - } + return contentTypeNames.Where(x => !string.IsNullOrWhiteSpace(x) && contentTypeDefinitions.ContainsKey(x)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentPublishedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentPublishedEventDisplayDriver.cs index badc5d8ecdc..58d31d59044 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentPublishedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentPublishedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentPublishedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentPublishedEventDisplayDriver : ContentEventDisplayDriver + public ContentPublishedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentPublishedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentTaskDisplayDriver.cs index 77f47867e41..69ecc25b340 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentTaskDisplayDriver.cs @@ -5,16 +5,15 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Workflows.Display; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public abstract class ContentTaskDisplayDriver : ActivityDisplayDriver where TActivity : ContentTask where TViewModel : ContentTaskViewModel, new() { - public abstract class ContentTaskDisplayDriver : ActivityDisplayDriver where TActivity : ContentTask where TViewModel : ContentTaskViewModel, new() + public override Task DisplayAsync(TActivity activity, BuildDisplayContext context) { - public override Task DisplayAsync(TActivity activity, BuildDisplayContext context) - { - return CombineAsync( - Shape($"{typeof(TActivity).Name}_Fields_Thumbnail", new ContentTaskViewModel(activity)).Location("Thumbnail", "Content"), - Shape($"{typeof(TActivity).Name}_Fields_Design", new ContentTaskViewModel(activity)).Location("Design", "Content") - ); - } + return CombineAsync( + Shape($"{typeof(TActivity).Name}_Fields_Thumbnail", new ContentTaskViewModel(activity)).Location("Thumbnail", "Content"), + Shape($"{typeof(TActivity).Name}_Fields_Design", new ContentTaskViewModel(activity)).Location("Design", "Content") + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUnpublishedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUnpublishedEventDisplayDriver.cs index 88e6d69b71b..1861010774b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUnpublishedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUnpublishedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentUnpublishedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentUnpublishedEventDisplayDriver : ContentEventDisplayDriver + public ContentUnpublishedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentUnpublishedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUpdatedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUpdatedEventDisplayDriver.cs index c37dddf3af6..14903691137 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUpdatedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentUpdatedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentUpdatedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentUpdatedEventDisplayDriver : ContentEventDisplayDriver + public ContentUpdatedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentUpdatedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentVersionedEventDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentVersionedEventDisplayDriver.cs index 7eadd4f8b70..f87a3c5f4df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentVersionedEventDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/ContentVersionedEventDisplayDriver.cs @@ -2,13 +2,12 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Contents.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class ContentVersionedEventDisplayDriver : ContentEventDisplayDriver { - public sealed class ContentVersionedEventDisplayDriver : ContentEventDisplayDriver + public ContentVersionedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) + : base(contentDefinitionManager) { - public ContentVersionedEventDisplayDriver(IContentDefinitionManager contentDefinitionManager) - : base(contentDefinitionManager) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/CreateContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/CreateContentTaskDisplayDriver.cs index f34275d86d4..2c96bce4cd8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/CreateContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/CreateContentTaskDisplayDriver.cs @@ -6,33 +6,32 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class CreateContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class CreateContentTaskDisplayDriver : ContentTaskDisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public CreateContentTaskDisplayDriver(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public CreateContentTaskDisplayDriver(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - protected override async ValueTask EditActivityAsync(CreateContentTask activity, CreateContentTaskViewModel model) - { - model.AvailableContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Select(x => new SelectListItem { Text = x.DisplayName, Value = x.Name }) - .ToList(); + protected override async ValueTask EditActivityAsync(CreateContentTask activity, CreateContentTaskViewModel model) + { + model.AvailableContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Select(x => new SelectListItem { Text = x.DisplayName, Value = x.Name }) + .ToList(); - model.ContentType = activity.ContentType; - model.Publish = activity.Publish; - model.ContentProperties = activity.ContentProperties.Expression; - } + model.ContentType = activity.ContentType; + model.Publish = activity.Publish; + model.ContentProperties = activity.ContentProperties.Expression; + } - protected override void UpdateActivity(CreateContentTaskViewModel model, CreateContentTask activity) - { - activity.ContentType = model.ContentType; - activity.Publish = model.Publish; - activity.ContentProperties = new WorkflowExpression(model.ContentProperties); - } + protected override void UpdateActivity(CreateContentTaskViewModel model, CreateContentTask activity) + { + activity.ContentType = model.ContentType; + activity.Publish = model.Publish; + activity.ContentProperties = new WorkflowExpression(model.ContentProperties); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/DeleteContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/DeleteContentTaskDisplayDriver.cs index 0e8d678413f..77258c12570 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/DeleteContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/DeleteContentTaskDisplayDriver.cs @@ -3,18 +3,17 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class DeleteContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class DeleteContentTaskDisplayDriver : ContentTaskDisplayDriver + protected override void EditActivity(DeleteContentTask activity, DeleteContentTaskViewModel model) { - protected override void EditActivity(DeleteContentTask activity, DeleteContentTaskViewModel model) - { - model.Expression = activity.Content.Expression; - } + model.Expression = activity.Content.Expression; + } - protected override void UpdateActivity(DeleteContentTaskViewModel model, DeleteContentTask activity) - { - activity.Content = new WorkflowExpression(model.Expression); - } + protected override void UpdateActivity(DeleteContentTaskViewModel model, DeleteContentTask activity) + { + activity.Content = new WorkflowExpression(model.Expression); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/PublishContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/PublishContentTaskDisplayDriver.cs index 8b07fab5c28..3bc4d9b7174 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/PublishContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/PublishContentTaskDisplayDriver.cs @@ -3,18 +3,17 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class PublishContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class PublishContentTaskDisplayDriver : ContentTaskDisplayDriver + protected override void EditActivity(PublishContentTask activity, PublishContentTaskViewModel model) { - protected override void EditActivity(PublishContentTask activity, PublishContentTaskViewModel model) - { - model.Expression = activity.Content.Expression; - } + model.Expression = activity.Content.Expression; + } - protected override void UpdateActivity(PublishContentTaskViewModel model, PublishContentTask activity) - { - activity.Content = new WorkflowExpression(model.Expression); - } + protected override void UpdateActivity(PublishContentTaskViewModel model, PublishContentTask activity) + { + activity.Content = new WorkflowExpression(model.Expression); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/RetrieveContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/RetrieveContentTaskDisplayDriver.cs index 778debd55fe..b19fe01c5df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/RetrieveContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/RetrieveContentTaskDisplayDriver.cs @@ -3,18 +3,17 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class RetrieveContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class RetrieveContentTaskDisplayDriver : ContentTaskDisplayDriver + protected override void EditActivity(RetrieveContentTask activity, RetrieveContentTaskViewModel model) { - protected override void EditActivity(RetrieveContentTask activity, RetrieveContentTaskViewModel model) - { - model.ContentItemId = activity.Content.Expression; - } + model.ContentItemId = activity.Content.Expression; + } - protected override void UpdateActivity(RetrieveContentTaskViewModel model, RetrieveContentTask activity) - { - activity.Content = new WorkflowExpression(model.ContentItemId); - } + protected override void UpdateActivity(RetrieveContentTaskViewModel model, RetrieveContentTask activity) + { + activity.Content = new WorkflowExpression(model.ContentItemId); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UnpublishContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UnpublishContentTaskDisplayDriver.cs index 76ff135f1ce..a1de0090bff 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UnpublishContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UnpublishContentTaskDisplayDriver.cs @@ -3,18 +3,17 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class UnpublishContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class UnpublishContentTaskDisplayDriver : ContentTaskDisplayDriver + protected override void EditActivity(UnpublishContentTask activity, UnpublishContentTaskViewModel model) { - protected override void EditActivity(UnpublishContentTask activity, UnpublishContentTaskViewModel model) - { - model.Expression = activity.Content.Expression; - } + model.Expression = activity.Content.Expression; + } - protected override void UpdateActivity(UnpublishContentTaskViewModel model, UnpublishContentTask activity) - { - activity.Content = new WorkflowExpression(model.Expression); - } + protected override void UpdateActivity(UnpublishContentTaskViewModel model, UnpublishContentTask activity) + { + activity.Content = new WorkflowExpression(model.Expression); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UpdateContentTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UpdateContentTaskDisplayDriver.cs index 538faeac9e6..8996ce4b7d2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UpdateContentTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Drivers/UpdateContentTaskDisplayDriver.cs @@ -7,33 +7,32 @@ using OrchardCore.Contents.Workflows.ViewModels; using OrchardCore.Workflows.Models; -namespace OrchardCore.Contents.Workflows.Drivers +namespace OrchardCore.Contents.Workflows.Drivers; + +public sealed class UpdateContentTaskDisplayDriver : ContentTaskDisplayDriver { - public sealed class UpdateContentTaskDisplayDriver : ContentTaskDisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public UpdateContentTaskDisplayDriver(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public UpdateContentTaskDisplayDriver(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - protected override async ValueTask EditActivityAsync(UpdateContentTask activity, UpdateContentTaskViewModel model) - { - model.AvailableContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Select(x => new SelectListItem { Text = x.DisplayName, Value = x.Name }) - .ToList(); + protected override async ValueTask EditActivityAsync(UpdateContentTask activity, UpdateContentTaskViewModel model) + { + model.AvailableContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Select(x => new SelectListItem { Text = x.DisplayName, Value = x.Name }) + .ToList(); - model.ContentItemIdExpression = activity.Content.Expression; - model.ContentProperties = activity.ContentProperties.Expression; - model.Publish = activity.Publish; - } + model.ContentItemIdExpression = activity.Content.Expression; + model.ContentProperties = activity.ContentProperties.Expression; + model.Publish = activity.Publish; + } - protected override void UpdateActivity(UpdateContentTaskViewModel model, UpdateContentTask activity) - { - activity.Content = new WorkflowExpression(model.ContentItemIdExpression); - activity.ContentProperties = new WorkflowExpression(model.ContentProperties); - activity.Publish = model.Publish; - } + protected override void UpdateActivity(UpdateContentTaskViewModel model, UpdateContentTask activity) + { + activity.Content = new WorkflowExpression(model.ContentItemIdExpression); + activity.ContentProperties = new WorkflowExpression(model.ContentProperties); + activity.Publish = model.Publish; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentItemSerializer.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentItemSerializer.cs index 19088dd534b..de90eb10f10 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentItemSerializer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentItemSerializer.cs @@ -3,43 +3,42 @@ using OrchardCore.ContentManagement; using OrchardCore.Workflows.Models; -namespace OrchardCore.Workflows.Services +namespace OrchardCore.Workflows.Services; + +public class ContentItemSerializer : IWorkflowValueSerializer { - public class ContentItemSerializer : IWorkflowValueSerializer + private readonly IContentManager _contentManager; + + public ContentItemSerializer(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public ContentItemSerializer(IContentManager contentManager) + public async Task DeserializeValueAsync(SerializeWorkflowValueContext context) + { + if (context.Input is JsonObject jObject) { - _contentManager = contentManager; - } + var type = jObject.Value("Type"); - public async Task DeserializeValueAsync(SerializeWorkflowValueContext context) - { - if (context.Input is JsonObject jObject) + if (type == "Content") { - var type = jObject.Value("Type"); - - if (type == "Content") - { - var contentId = jObject.Value("ContentId"); - context.Output = contentId != null ? await _contentManager.GetAsync(contentId, VersionOptions.Latest) : default(IContent); - } + var contentId = jObject.Value("ContentId"); + context.Output = contentId != null ? await _contentManager.GetAsync(contentId, VersionOptions.Latest) : default(IContent); } } + } - public Task SerializeValueAsync(SerializeWorkflowValueContext context) + public Task SerializeValueAsync(SerializeWorkflowValueContext context) + { + if (context.Input is IContent content) { - if (context.Input is IContent content) + context.Output = JObject.FromObject(new { - context.Output = JObject.FromObject(new - { - Type = "Content", - ContentId = content.ContentItem.ContentItemId - }); - } - - return Task.CompletedTask; + Type = "Content", + ContentId = content.ContentItem.ContentItemId + }); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentsHandler.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentsHandler.cs index 51f1cff6830..c42bc653ecd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentsHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Handlers/ContentsHandler.cs @@ -7,69 +7,68 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows.Handlers +namespace OrchardCore.Contents.Workflows.Handlers; + +public class ContentsHandler : ContentHandlerBase { - public class ContentsHandler : ContentHandlerBase + private readonly IWorkflowManager _workflowManager; + + public ContentsHandler(IWorkflowManager workflowManager) { - private readonly IWorkflowManager _workflowManager; + _workflowManager = workflowManager; + } - public ContentsHandler(IWorkflowManager workflowManager) - { - _workflowManager = workflowManager; - } + public override Task CreatedAsync(CreateContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentCreatedEvent), context.ContentItem); + } - public override Task CreatedAsync(CreateContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentCreatedEvent), context.ContentItem); - } + public override Task UpdatedAsync(UpdateContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentUpdatedEvent), context.ContentItem); + } - public override Task UpdatedAsync(UpdateContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentUpdatedEvent), context.ContentItem); - } + public override Task DraftSavedAsync(SaveDraftContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentDraftSavedEvent), context.ContentItem); + } - public override Task DraftSavedAsync(SaveDraftContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentDraftSavedEvent), context.ContentItem); - } + public override Task PublishedAsync(PublishContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentPublishedEvent), context.ContentItem); + } - public override Task PublishedAsync(PublishContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentPublishedEvent), context.ContentItem); - } + public override Task UnpublishedAsync(PublishContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentUnpublishedEvent), context.ContentItem); + } - public override Task UnpublishedAsync(PublishContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentUnpublishedEvent), context.ContentItem); - } + public override Task RemovedAsync(RemoveContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentDeletedEvent), context.ContentItem); + } - public override Task RemovedAsync(RemoveContentContext context) - { - return TriggerWorkflowEventAsync(nameof(ContentDeletedEvent), context.ContentItem); - } + public override Task VersionedAsync(VersionContentContext context) + { + return TriggerWorkflowEventAsync(nameof(ContentVersionedEvent), context.ContentItem); + } - public override Task VersionedAsync(VersionContentContext context) + private Task> TriggerWorkflowEventAsync(string name, ContentItem contentItem) + { + var contentEvent = new ContentEventContext() { - return TriggerWorkflowEventAsync(nameof(ContentVersionedEvent), context.ContentItem); - } + Name = name, + ContentType = contentItem.ContentType, + ContentItemId = contentItem.ContentItemId, + ContentItemVersionId = contentItem.ContentItemVersionId, + }; - private Task> TriggerWorkflowEventAsync(string name, ContentItem contentItem) + var input = new Dictionary { - var contentEvent = new ContentEventContext() - { - Name = name, - ContentType = contentItem.ContentType, - ContentItemId = contentItem.ContentItemId, - ContentItemVersionId = contentItem.ContentItemVersionId, - }; - - var input = new Dictionary - { - { ContentEventConstants.ContentItemInputKey, contentItem }, - { ContentEventConstants.ContentEventInputKey, contentEvent }, - }; + { ContentEventConstants.ContentItemInputKey, contentItem }, + { ContentEventConstants.ContentEventInputKey, contentEvent }, + }; - return _workflowManager.TriggerEventAsync(name, input, correlationId: contentItem.ContentItemId); - } + return _workflowManager.TriggerEventAsync(name, input, correlationId: contentItem.ContentItemId); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Startup.cs index 9a0ae65022a..4bd3a50d67f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/Startup.cs @@ -7,29 +7,28 @@ using OrchardCore.Workflows.Helpers; using OrchardCore.Workflows.Services; -namespace OrchardCore.Contents.Workflows +namespace OrchardCore.Contents.Workflows; + +[RequireFeatures("OrchardCore.Workflows")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Workflows")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentCreatedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentCreatedEventViewModel.cs index a956ba1d36a..ea308ff2f3d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentCreatedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentCreatedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentCreatedEventViewModel : ContentEventViewModel { - public class ContentCreatedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDeletedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDeletedEventViewModel.cs index bbb5f02173d..c498dd68c61 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDeletedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDeletedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentDeletedEventViewModel : ContentEventViewModel { - public class ContentDeletedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDraftSavedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDraftSavedEventViewModel.cs index 5dee5e7870e..fc11b585e3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDraftSavedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentDraftSavedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentDraftSavedEventViewModel : ContentEventViewModel { - public class ContentDraftSavedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentEventViewModel.cs index e37774ca6cf..f5832fa29d1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentEventViewModel.cs @@ -3,20 +3,19 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentEventViewModel : ActivityViewModel where T : ContentEvent { - public class ContentEventViewModel : ActivityViewModel where T : ContentEvent + public ContentEventViewModel() { - public ContentEventViewModel() - { - } - - public ContentEventViewModel(T activity) - { - Activity = activity; - } + } - public IList ContentTypeFilter { get; set; } - public IList SelectedContentTypeNames { get; set; } = []; + public ContentEventViewModel(T activity) + { + Activity = activity; } + + public IList ContentTypeFilter { get; set; } + public IList SelectedContentTypeNames { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentPublishedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentPublishedEventViewModel.cs index b3c557c06f6..f3ee0484977 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentPublishedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentPublishedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentPublishedEventViewModel : ContentEventViewModel { - public class ContentPublishedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentTaskViewModel.cs index 747fb4fd6ff..7883a0d4105 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentTaskViewModel.cs @@ -1,17 +1,16 @@ using OrchardCore.Contents.Workflows.Activities; using OrchardCore.Workflows.ViewModels; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentTaskViewModel : ActivityViewModel where T : ContentTask { - public class ContentTaskViewModel : ActivityViewModel where T : ContentTask + public ContentTaskViewModel() { - public ContentTaskViewModel() - { - } + } - public ContentTaskViewModel(T activity) - { - Activity = activity; - } + public ContentTaskViewModel(T activity) + { + Activity = activity; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUnpublishedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUnpublishedEventViewModel.cs index cb308a2e45d..f776dec36da 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUnpublishedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUnpublishedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentUnpublishedEventViewModel : ContentEventViewModel { - public class ContentUnpublishedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUpdatedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUpdatedEventViewModel.cs index 7ddd6c7d325..fb93ff70cdc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUpdatedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentUpdatedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentUpdatedEventViewModel : ContentEventViewModel { - public class ContentUpdatedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentVersionedEventViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentVersionedEventViewModel.cs index 252568de282..3d3b580aa0b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentVersionedEventViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/ContentVersionedEventViewModel.cs @@ -1,8 +1,7 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class ContentVersionedEventViewModel : ContentEventViewModel { - public class ContentVersionedEventViewModel : ContentEventViewModel - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/CreateContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/CreateContentTaskViewModel.cs index ae63befb516..982b6ca6623 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/CreateContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/CreateContentTaskViewModel.cs @@ -3,15 +3,14 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class CreateContentTaskViewModel : ContentTaskViewModel { - public class CreateContentTaskViewModel : ContentTaskViewModel - { - [BindNever] - public IList AvailableContentTypes { get; set; } + [BindNever] + public IList AvailableContentTypes { get; set; } - public string ContentType { get; set; } - public bool Publish { get; set; } - public string ContentProperties { get; set; } - } + public string ContentType { get; set; } + public bool Publish { get; set; } + public string ContentProperties { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/DeleteContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/DeleteContentTaskViewModel.cs index d153ed72511..82bb574b516 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/DeleteContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/DeleteContentTaskViewModel.cs @@ -1,12 +1,11 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class DeleteContentTaskViewModel : ContentTaskViewModel { - public class DeleteContentTaskViewModel : ContentTaskViewModel - { - /// - /// The expression resulting into a content item or content item ID to delete. - /// - public string Expression { get; set; } - } + /// + /// The expression resulting into a content item or content item ID to delete. + /// + public string Expression { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/PublishContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/PublishContentTaskViewModel.cs index bb4464a8d80..7ed4eb80450 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/PublishContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/PublishContentTaskViewModel.cs @@ -1,12 +1,11 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class PublishContentTaskViewModel : ContentTaskViewModel { - public class PublishContentTaskViewModel : ContentTaskViewModel - { - /// - /// The expression resulting into a content item or content item ID to publish. - /// - public string Expression { get; set; } - } + /// + /// The expression resulting into a content item or content item ID to publish. + /// + public string Expression { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/RetrieveContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/RetrieveContentTaskViewModel.cs index 2475fe517d7..59f83b89e5f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/RetrieveContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/RetrieveContentTaskViewModel.cs @@ -1,12 +1,11 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class RetrieveContentTaskViewModel : ContentTaskViewModel { - public class RetrieveContentTaskViewModel : ContentTaskViewModel - { - /// - /// The expression resulting into a content item ID to retrieve. - /// - public string ContentItemId { get; set; } - } + /// + /// The expression resulting into a content item ID to retrieve. + /// + public string ContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UnpublishContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UnpublishContentTaskViewModel.cs index 582392b4d3d..3ae63ab5a39 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UnpublishContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UnpublishContentTaskViewModel.cs @@ -1,12 +1,11 @@ using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class UnpublishContentTaskViewModel : ContentTaskViewModel { - public class UnpublishContentTaskViewModel : ContentTaskViewModel - { - /// - /// The expression resulting into a content item or content item ID to unpublish. - /// - public string Expression { get; set; } - } + /// + /// The expression resulting into a content item or content item ID to unpublish. + /// + public string Expression { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UpdateContentTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UpdateContentTaskViewModel.cs index d15af4a6c86..8e87d4d9e07 100644 --- a/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UpdateContentTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Contents/Workflows/ViewModels/UpdateContentTaskViewModel.cs @@ -3,16 +3,15 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Contents.Workflows.Activities; -namespace OrchardCore.Contents.Workflows.ViewModels +namespace OrchardCore.Contents.Workflows.ViewModels; + +public class UpdateContentTaskViewModel : ContentTaskViewModel { - public class UpdateContentTaskViewModel : ContentTaskViewModel - { - [BindNever] - public IList AvailableContentTypes { get; set; } + [BindNever] + public IList AvailableContentTypes { get; set; } - public string ContentType { get; set; } - public string ContentItemIdExpression { get; set; } - public string ContentProperties { get; set; } - public bool Publish { get; set; } - } + public string ContentType { get; set; } + public string ContentItemIdExpression { get; set; } + public string ContentProperties { get; set; } + public bool Publish { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Cors/AdminMenu.cs index 1dd569ae64b..0b94d93cb00 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/AdminMenu.cs @@ -2,38 +2,37 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Cors +namespace OrchardCore.Cors; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["CORS"], S["CORS"].PrefixPosition(), entry => entry - .AddClass("cors") - .Id("cors") - .Action("Index", "Admin", "OrchardCore.Cors") - .Permission(Permissions.ManageCorsSettings) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["CORS"], S["CORS"].PrefixPosition(), entry => entry + .AddClass("cors") + .Id("cors") + .Action("Index", "Admin", "OrchardCore.Cors") + .Permission(Permissions.ManageCorsSettings) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Controllers/AdminController.cs index e64c41eea4a..cdf676e2c9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Controllers/AdminController.cs @@ -12,134 +12,133 @@ using OrchardCore.DisplayManagement.Notify; using OrchardCore.Environment.Shell; -namespace OrchardCore.Cors.Controllers +namespace OrchardCore.Cors.Controllers; + +[Admin] +public class AdminController : Controller { - [Admin] - public class AdminController : Controller + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IAuthorizationService _authorizationService; + private readonly CorsService _corsService; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public AdminController( + IShellHost shellHost, + ShellSettings shellSettings, + IAuthorizationService authorizationService, + CorsService corsService, + INotifier notifier, + IHtmlLocalizer htmlLocalizer + ) { - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IAuthorizationService _authorizationService; - private readonly CorsService _corsService; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - - public AdminController( - IShellHost shellHost, - ShellSettings shellSettings, - IAuthorizationService authorizationService, - CorsService corsService, - INotifier notifier, - IHtmlLocalizer htmlLocalizer - ) - { - _shellHost = shellHost; - _shellSettings = shellSettings; - _authorizationService = authorizationService; - _corsService = corsService; - _notifier = notifier; - H = htmlLocalizer; - } + _shellHost = shellHost; + _shellSettings = shellSettings; + _authorizationService = authorizationService; + _corsService = corsService; + _notifier = notifier; + H = htmlLocalizer; + } - [HttpGet] - [Admin("Cors", "CorsIndex")] - public async Task Index() + [HttpGet] + [Admin("Cors", "CorsIndex")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageCorsSettings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageCorsSettings)) - { - return Unauthorized(); - } + return Unauthorized(); + } - var settings = await _corsService.GetSettingsAsync(); + var settings = await _corsService.GetSettingsAsync(); - var list = new List(); + var list = new List(); - if (settings?.Policies != null) + if (settings?.Policies != null) + { + foreach (var policySetting in settings.Policies) { - foreach (var policySetting in settings.Policies) + var policyViewModel = new CorsPolicyViewModel() { - var policyViewModel = new CorsPolicyViewModel() - { - Name = policySetting.Name, - AllowAnyHeader = policySetting.AllowAnyHeader, - AllowedHeaders = policySetting.AllowedHeaders, - AllowAnyMethod = policySetting.AllowAnyMethod, - AllowedMethods = policySetting.AllowedMethods, - AllowAnyOrigin = policySetting.AllowAnyOrigin, - AllowedOrigins = policySetting.AllowedOrigins, - AllowCredentials = policySetting.AllowCredentials, - IsDefaultPolicy = policySetting.IsDefaultPolicy, - ExposedHeaders = policySetting.ExposedHeaders, - }; - - list.Add(policyViewModel); - } + Name = policySetting.Name, + AllowAnyHeader = policySetting.AllowAnyHeader, + AllowedHeaders = policySetting.AllowedHeaders, + AllowAnyMethod = policySetting.AllowAnyMethod, + AllowedMethods = policySetting.AllowedMethods, + AllowAnyOrigin = policySetting.AllowAnyOrigin, + AllowedOrigins = policySetting.AllowedOrigins, + AllowCredentials = policySetting.AllowCredentials, + IsDefaultPolicy = policySetting.IsDefaultPolicy, + ExposedHeaders = policySetting.ExposedHeaders, + }; + + list.Add(policyViewModel); } + } - var viewModel = new CorsSettingsViewModel - { - Policies = list.ToArray() - }; + var viewModel = new CorsSettingsViewModel + { + Policies = list.ToArray() + }; - return View(viewModel); - } + return View(viewModel); + } - [HttpPost] - [ActionName(nameof(Index))] - public async Task IndexPOST() + [HttpPost] + [ActionName(nameof(Index))] + public async Task IndexPOST() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageCorsSettings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageCorsSettings)) - { - return Unauthorized(); - } + return Unauthorized(); + } - var model = new CorsSettingsViewModel(); - var configJson = Request.Form["CorsSettings"].First(); - model.Policies = JConvert.DeserializeObject(configJson); + var model = new CorsSettingsViewModel(); + var configJson = Request.Form["CorsSettings"].First(); + model.Policies = JConvert.DeserializeObject(configJson); - var corsPolicies = new List(); + var corsPolicies = new List(); - // If "allow origin" and "allow credentials" are both true, issue a warning about CORS functionality. Inform the user. - var policyWarnings = new List(); + // If "allow origin" and "allow credentials" are both true, issue a warning about CORS functionality. Inform the user. + var policyWarnings = new List(); - foreach (var settingViewModel in model.Policies) + foreach (var settingViewModel in model.Policies) + { + corsPolicies.Add(new CorsPolicySetting { - corsPolicies.Add(new CorsPolicySetting - { - Name = settingViewModel.Name, - AllowAnyHeader = settingViewModel.AllowAnyHeader, - AllowAnyMethod = settingViewModel.AllowAnyMethod, - AllowAnyOrigin = settingViewModel.AllowAnyOrigin, - AllowCredentials = settingViewModel.AllowCredentials, - AllowedHeaders = settingViewModel.AllowedHeaders, - AllowedMethods = settingViewModel.AllowedMethods, - AllowedOrigins = settingViewModel.AllowedOrigins, - IsDefaultPolicy = settingViewModel.IsDefaultPolicy, - ExposedHeaders = settingViewModel.ExposedHeaders, - }); - - if (settingViewModel.AllowAnyOrigin && settingViewModel.AllowCredentials) - { - policyWarnings.Add(settingViewModel.Name); - } - } - var corsSettings = new CorsSettings() + Name = settingViewModel.Name, + AllowAnyHeader = settingViewModel.AllowAnyHeader, + AllowAnyMethod = settingViewModel.AllowAnyMethod, + AllowAnyOrigin = settingViewModel.AllowAnyOrigin, + AllowCredentials = settingViewModel.AllowCredentials, + AllowedHeaders = settingViewModel.AllowedHeaders, + AllowedMethods = settingViewModel.AllowedMethods, + AllowedOrigins = settingViewModel.AllowedOrigins, + IsDefaultPolicy = settingViewModel.IsDefaultPolicy, + ExposedHeaders = settingViewModel.ExposedHeaders, + }); + + if (settingViewModel.AllowAnyOrigin && settingViewModel.AllowCredentials) { - Policies = corsPolicies - }; - - await _corsService.UpdateSettingsAsync(corsSettings); + policyWarnings.Add(settingViewModel.Name); + } + } + var corsSettings = new CorsSettings() + { + Policies = corsPolicies + }; - await _shellHost.ReleaseShellContextAsync(_shellSettings); + await _corsService.UpdateSettingsAsync(corsSettings); - await _notifier.SuccessAsync(H["The CORS settings have updated successfully."]); + await _shellHost.ReleaseShellContextAsync(_shellSettings); - if (policyWarnings.Count > 0) - { - await _notifier.WarningAsync(H["Specifying {0} and {1} is an insecure configuration and can result in cross-site request forgery. The CORS service returns an invalid CORS response when an app is configured with both methods.
Affected policies: {2}
Refer to docs:https://learn.microsoft.com/en-us/aspnet/core/security/cors", "AllowAnyOrigin", "AllowCredentias", string.Join(", ", policyWarnings) ]); - } + await _notifier.SuccessAsync(H["The CORS settings have updated successfully."]); - return View(model); + if (policyWarnings.Count > 0) + { + await _notifier.WarningAsync(H["Specifying {0} and {1} is an insecure configuration and can result in cross-site request forgery. The CORS service returns an invalid CORS response when an app is configured with both methods.
Affected policies: {2}
Refer to docs:https://learn.microsoft.com/en-us/aspnet/core/security/cors", "AllowAnyOrigin", "AllowCredentias", string.Join(", ", policyWarnings)]); } + + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsOptionsConfiguration.cs index 47700a3dd81..a04ae7cbb70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsOptionsConfiguration.cs @@ -3,86 +3,85 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace OrchardCore.Cors.Services +namespace OrchardCore.Cors.Services; + +public sealed class CorsOptionsConfiguration : IConfigureOptions { - public sealed class CorsOptionsConfiguration : IConfigureOptions + private readonly CorsService _corsService; + private readonly ILogger _logger; + + public CorsOptionsConfiguration(CorsService corsService, ILogger logger) { - private readonly CorsService _corsService; - private readonly ILogger _logger; + _corsService = corsService; + _logger = logger; + } - public CorsOptionsConfiguration(CorsService corsService, ILogger logger) + public void Configure(CorsOptions options) + { + var corsSettings = _corsService.GetSettingsAsync().GetAwaiter().GetResult(); + if (corsSettings?.Policies == null || !corsSettings.Policies.Any()) { - _corsService = corsService; - _logger = logger; + return; } - public void Configure(CorsOptions options) + foreach (var corsPolicy in corsSettings.Policies) { - var corsSettings = _corsService.GetSettingsAsync().GetAwaiter().GetResult(); - if (corsSettings?.Policies == null || !corsSettings.Policies.Any()) + if (corsPolicy.AllowCredentials && corsPolicy.AllowAnyOrigin) { - return; + _logger.LogWarning("Using AllowCredentials and AllowAnyOrigin at the same time is considered a security risk, the {PolicyName} policy will not be loaded.", corsPolicy.Name); + continue; } - foreach (var corsPolicy in corsSettings.Policies) + options.AddPolicy(corsPolicy.Name, configurePolicy => { - if (corsPolicy.AllowCredentials && corsPolicy.AllowAnyOrigin) + if (corsPolicy.AllowAnyHeader) { - _logger.LogWarning("Using AllowCredentials and AllowAnyOrigin at the same time is considered a security risk, the {PolicyName} policy will not be loaded.", corsPolicy.Name); - continue; + configurePolicy.AllowAnyHeader(); } - - options.AddPolicy(corsPolicy.Name, configurePolicy => + else { - if (corsPolicy.AllowAnyHeader) - { - configurePolicy.AllowAnyHeader(); - } - else - { - configurePolicy.WithHeaders(corsPolicy.AllowedHeaders); - } - - if (corsPolicy.AllowAnyMethod) - { - configurePolicy.AllowAnyMethod(); - } - else - { - configurePolicy.WithMethods(corsPolicy.AllowedMethods); - } + configurePolicy.WithHeaders(corsPolicy.AllowedHeaders); + } - if (corsPolicy.AllowAnyOrigin) - { - configurePolicy.AllowAnyOrigin(); - } - else - { - configurePolicy.WithOrigins(corsPolicy.AllowedOrigins); - } + if (corsPolicy.AllowAnyMethod) + { + configurePolicy.AllowAnyMethod(); + } + else + { + configurePolicy.WithMethods(corsPolicy.AllowedMethods); + } - if (corsPolicy.AllowCredentials) - { - configurePolicy.AllowCredentials(); - } - else - { - configurePolicy.DisallowCredentials(); - } + if (corsPolicy.AllowAnyOrigin) + { + configurePolicy.AllowAnyOrigin(); + } + else + { + configurePolicy.WithOrigins(corsPolicy.AllowedOrigins); + } - if (corsPolicy.ExposedHeaders?.Length > 0) - { - configurePolicy.WithExposedHeaders(corsPolicy.ExposedHeaders); - } - }); + if (corsPolicy.AllowCredentials) + { + configurePolicy.AllowCredentials(); + } + else + { + configurePolicy.DisallowCredentials(); + } - if (corsPolicy.IsDefaultPolicy) + if (corsPolicy.ExposedHeaders?.Length > 0) { - options.DefaultPolicyName = corsPolicy.Name; + configurePolicy.WithExposedHeaders(corsPolicy.ExposedHeaders); } - } + }); - options.DefaultPolicyName ??= corsSettings.Policies.FirstOrDefault()?.Name; + if (corsPolicy.IsDefaultPolicy) + { + options.DefaultPolicyName = corsPolicy.Name; + } } + + options.DefaultPolicyName ??= corsSettings.Policies.FirstOrDefault()?.Name; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsService.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsService.cs index ac3f97f58fc..2c76730215c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Services/CorsService.cs @@ -3,27 +3,26 @@ using OrchardCore.Cors.Settings; using OrchardCore.Settings; -namespace OrchardCore.Cors.Services +namespace OrchardCore.Cors.Services; + +public class CorsService { - public class CorsService - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public CorsService(ISiteService siteService) - { - _siteService = siteService; - } + public CorsService(ISiteService siteService) + { + _siteService = siteService; + } - public async Task GetSettingsAsync() - { - return await _siteService.GetSettingsAsync(); - } + public async Task GetSettingsAsync() + { + return await _siteService.GetSettingsAsync(); + } - internal async Task UpdateSettingsAsync(CorsSettings corsSettings) - { - var siteSettings = await _siteService.LoadSiteSettingsAsync(); - siteSettings.Properties[nameof(CorsSettings)] = JObject.FromObject(corsSettings); - await _siteService.UpdateSiteSettingsAsync(siteSettings); - } + internal async Task UpdateSettingsAsync(CorsSettings corsSettings) + { + var siteSettings = await _siteService.LoadSiteSettingsAsync(); + siteSettings.Properties[nameof(CorsSettings)] = JObject.FromObject(corsSettings); + await _siteService.UpdateSiteSettingsAsync(siteSettings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsPolicySetting.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsPolicySetting.cs index 270e2405d4e..4f09a6e496b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsPolicySetting.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsPolicySetting.cs @@ -1,25 +1,24 @@ -namespace OrchardCore.Cors.Settings +namespace OrchardCore.Cors.Settings; + +public class CorsPolicySetting { - public class CorsPolicySetting - { - public string Name { get; set; } + public string Name { get; set; } - public bool AllowAnyOrigin { get; set; } + public bool AllowAnyOrigin { get; set; } - public string[] AllowedOrigins { get; set; } + public string[] AllowedOrigins { get; set; } - public bool AllowAnyHeader { get; set; } + public bool AllowAnyHeader { get; set; } - public string[] AllowedHeaders { get; set; } + public string[] AllowedHeaders { get; set; } - public bool AllowAnyMethod { get; set; } + public bool AllowAnyMethod { get; set; } - public string[] AllowedMethods { get; set; } + public string[] AllowedMethods { get; set; } - public bool AllowCredentials { get; set; } + public bool AllowCredentials { get; set; } - public bool IsDefaultPolicy { get; set; } + public bool IsDefaultPolicy { get; set; } - public string[] ExposedHeaders { get; set; } - } + public string[] ExposedHeaders { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsSettings.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsSettings.cs index fbdd6b970f8..6be043988fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Settings/CorsSettings.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace OrchardCore.Cors.Settings +namespace OrchardCore.Cors.Settings; + +public class CorsSettings { - public class CorsSettings - { - public IEnumerable Policies { get; set; } - } + public IEnumerable Policies { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Cors/Startup.cs index 043636cc2da..2b3d2b0a756 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/Startup.cs @@ -11,26 +11,25 @@ using OrchardCore.Security.Permissions; using CorsService = OrchardCore.Cors.Services.CorsService; -namespace OrchardCore.Cors +namespace OrchardCore.Cors; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - public override int Order - => OrchardCoreConstants.ConfigureOrder.Cors; + public override int Order + => OrchardCoreConstants.ConfigureOrder.Cors; - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseCors(); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseCors(); + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddSingleton(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); - services.TryAddEnumerable(ServiceDescriptor - .Transient, CorsOptionsConfiguration>()); - } + services.TryAddEnumerable(ServiceDescriptor + .Transient, CorsOptionsConfiguration>()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsPolicyViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsPolicyViewModel.cs index e9022f6ae4f..6b802f2aee7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsPolicyViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsPolicyViewModel.cs @@ -1,25 +1,24 @@ -namespace OrchardCore.Cors.ViewModels +namespace OrchardCore.Cors.ViewModels; + +public class CorsPolicyViewModel { - public class CorsPolicyViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public bool AllowAnyOrigin { get; set; } + public bool AllowAnyOrigin { get; set; } - public string[] AllowedOrigins { get; set; } + public string[] AllowedOrigins { get; set; } - public bool AllowAnyHeader { get; set; } + public bool AllowAnyHeader { get; set; } - public string[] AllowedHeaders { get; set; } + public string[] AllowedHeaders { get; set; } - public bool AllowAnyMethod { get; set; } + public bool AllowAnyMethod { get; set; } - public string[] AllowedMethods { get; set; } + public string[] AllowedMethods { get; set; } - public bool AllowCredentials { get; set; } + public bool AllowCredentials { get; set; } - public bool IsDefaultPolicy { get; set; } + public bool IsDefaultPolicy { get; set; } - public string[] ExposedHeaders { get; set; } - } + public string[] ExposedHeaders { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsSettingsViewModel.cs index 6aedb5f8e3c..645668b4b72 100644 --- a/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Cors/ViewModels/CorsSettingsViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Cors.ViewModels +namespace OrchardCore.Cors.ViewModels; + +public class CorsSettingsViewModel { - public class CorsSettingsViewModel - { - public CorsPolicyViewModel[] Policies { get; set; } + public CorsPolicyViewModel[] Policies { get; set; } - public string DefaultPolicyName { get; set; } - } + public string DefaultPolicyName { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/AdminMenu.cs index 6a9e9d0f69d..4c55056bc7c 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/AdminMenu.cs @@ -6,60 +6,59 @@ using OrchardCore.Mvc.Utilities; using OrchardCore.Navigation; -namespace OrchardCore.CustomSettings +namespace OrchardCore.CustomSettings; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider - { - private static readonly ConcurrentDictionary _routeValues = []; + private static readonly ConcurrentDictionary _routeValues = []; + + private readonly CustomSettingsService _customSettingsService; - private readonly CustomSettingsService _customSettingsService; + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public AdminMenu( + IStringLocalizer localizer, + CustomSettingsService customSettingsService) + { + S = localizer; + _customSettingsService = customSettingsService; + } - public AdminMenu( - IStringLocalizer localizer, - CustomSettingsService customSettingsService) + public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; - _customSettingsService = customSettingsService; + return; } - public async Task BuildNavigationAsync(string name, NavigationBuilder builder) + foreach (var type in await _customSettingsService.GetAllSettingsTypesAsync()) { - if (!NavigationHelper.IsAdminMenu(name)) + if (!_routeValues.TryGetValue(type.Name, out var routeValues)) { - return; - } - - foreach (var type in await _customSettingsService.GetAllSettingsTypesAsync()) - { - if (!_routeValues.TryGetValue(type.Name, out var routeValues)) + routeValues = new RouteValueDictionary() { - routeValues = new RouteValueDictionary() - { - { "area", "OrchardCore.Settings" }, - { "groupId", type.Name }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", type.Name }, + }; - _routeValues[type.Name] = routeValues; - } + _routeValues[type.Name] = routeValues; + } - var htmlName = type.Name.HtmlClassify(); + var htmlName = type.Name.HtmlClassify(); - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(new LocalizedString(type.DisplayName, type.DisplayName), type.DisplayName.PrefixPosition(), layers => layers - .Action("Index", "Admin", routeValues) - .AddClass(htmlName) - .Id(htmlName) - .Permission(Permissions.CreatePermissionForType(type)) - .Resource(type.Name) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(new LocalizedString(type.DisplayName, type.DisplayName), type.DisplayName.PrefixPosition(), layers => layers + .Action("Index", "Admin", routeValues) + .AddClass(htmlName) + .Id(htmlName) + .Permission(Permissions.CreatePermissionForType(type)) + .Resource(type.Name) + .LocalNav() ) - ); - } + ) + ); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentSource.cs index 73382cf61d8..2c29159cafc 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentSource.cs @@ -5,50 +5,49 @@ using OrchardCore.CustomSettings.Services; using OrchardCore.Deployment; -namespace OrchardCore.CustomSettings.Deployment +namespace OrchardCore.CustomSettings.Deployment; + +public class CustomSettingsDeploymentSource : IDeploymentSource { - public class CustomSettingsDeploymentSource : IDeploymentSource + private readonly CustomSettingsService _customSettingsService; + + public CustomSettingsDeploymentSource(CustomSettingsService customSettingsService) { - private readonly CustomSettingsService _customSettingsService; + _customSettingsService = customSettingsService; + } - public CustomSettingsDeploymentSource(CustomSettingsService customSettingsService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var customSettingsStep = step as CustomSettingsDeploymentStep; + if (customSettingsStep == null) { - _customSettingsService = customSettingsService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var settingsList = new List> { - var customSettingsStep = step as CustomSettingsDeploymentStep; - if (customSettingsStep == null) - { - return; - } - - var settingsList = new List> - { - new("name", "custom-settings"), - }; + new("name", "custom-settings"), + }; - var settingsTypes = customSettingsStep.IncludeAll - ? (await _customSettingsService.GetAllSettingsTypesAsync()).ToArray() - : (await _customSettingsService.GetSettingsTypesAsync(customSettingsStep.SettingsTypeNames)).ToArray(); + var settingsTypes = customSettingsStep.IncludeAll + ? (await _customSettingsService.GetAllSettingsTypesAsync()).ToArray() + : (await _customSettingsService.GetSettingsTypesAsync(customSettingsStep.SettingsTypeNames)).ToArray(); - foreach (var settingsType in settingsTypes) - { - if (!await _customSettingsService.CanUserCreateSettingsAsync(settingsType)) - { - return; - } - } - - foreach (var settingsType in settingsTypes) + foreach (var settingsType in settingsTypes) + { + if (!await _customSettingsService.CanUserCreateSettingsAsync(settingsType)) { - var settings = await _customSettingsService.GetSettingsAsync(settingsType); - settingsList.Add(new(settings.ContentType, JObject.FromObject(settings))); + return; } + } - // Adding custom settings - result.Steps.Add(new JsonObject(settingsList.ToArray())); + foreach (var settingsType in settingsTypes) + { + var settings = await _customSettingsService.GetSettingsAsync(settingsType); + settingsList.Add(new(settings.ContentType, JObject.FromObject(settings))); } + + // Adding custom settings + result.Steps.Add(new JsonObject(settingsList.ToArray())); } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStep.cs index 46f3cc97542..1d425b64d86 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStep.cs @@ -1,16 +1,15 @@ using OrchardCore.Deployment; -namespace OrchardCore.CustomSettings.Deployment +namespace OrchardCore.CustomSettings.Deployment; + +public class CustomSettingsDeploymentStep : DeploymentStep { - public class CustomSettingsDeploymentStep : DeploymentStep + public CustomSettingsDeploymentStep() { - public CustomSettingsDeploymentStep() - { - Name = "CustomSettings"; - } + Name = "CustomSettings"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] SettingsTypeNames { get; set; } - } + public string[] SettingsTypeNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStepDriver.cs index 4fb417001fc..b950c78864d 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Deployment/CustomSettingsDeploymentStepDriver.cs @@ -6,52 +6,51 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.CustomSettings.Deployment +namespace OrchardCore.CustomSettings.Deployment; + +public sealed class CustomSettingsDeploymentStepDriver : DisplayDriver { - public sealed class CustomSettingsDeploymentStepDriver : DisplayDriver + private readonly CustomSettingsService _customSettingsService; + + public CustomSettingsDeploymentStepDriver(CustomSettingsService customSettingsService) { - private readonly CustomSettingsService _customSettingsService; + _customSettingsService = customSettingsService; + } - public CustomSettingsDeploymentStepDriver(CustomSettingsService customSettingsService) - { - _customSettingsService = customSettingsService; - } + public override Task DisplayAsync(CustomSettingsDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("CustomSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("CustomSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(CustomSettingsDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(CustomSettingsDeploymentStep step, BuildEditorContext context) + { + return Initialize("CustomSettingsDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("CustomSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("CustomSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.SettingsTypeNames = step.SettingsTypeNames; + model.AllSettingsTypeNames = (await _customSettingsService.GetAllSettingsTypeNamesAsync()).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(CustomSettingsDeploymentStep step, BuildEditorContext context) - { - return Initialize("CustomSettingsDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.SettingsTypeNames = step.SettingsTypeNames; - model.AllSettingsTypeNames = (await _customSettingsService.GetAllSettingsTypeNamesAsync()).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(CustomSettingsDeploymentStep step, UpdateEditorContext context) + { + step.SettingsTypeNames = []; + + await context.Updater.TryUpdateModelAsync(step, + Prefix, + x => x.SettingsTypeNames, + x => x.IncludeAll); - public override async Task UpdateAsync(CustomSettingsDeploymentStep step, UpdateEditorContext context) + // Don't have the selected option if include all. + if (step.IncludeAll) { step.SettingsTypeNames = []; - - await context.Updater.TryUpdateModelAsync(step, - Prefix, - x => x.SettingsTypeNames, - x => x.IncludeAll); - - // Don't have the selected option if include all. - if (step.IncludeAll) - { - step.SettingsTypeNames = []; - } - - return Edit(step, context); } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Drivers/CustomSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Drivers/CustomSettingsDisplayDriver.cs index d2c08f9e778..fe460555fba 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Drivers/CustomSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Drivers/CustomSettingsDisplayDriver.cs @@ -7,70 +7,69 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings; -namespace OrchardCore.CustomSettings.Drivers +namespace OrchardCore.CustomSettings.Drivers; + +/// +/// This driver generates an editor for site settings. The GroupId represents the type of +/// the settings to use. +/// +public sealed class CustomSettingsDisplayDriver : DisplayDriver { - /// - /// This driver generates an editor for site settings. The GroupId represents the type of - /// the settings to use. - /// - public sealed class CustomSettingsDisplayDriver : DisplayDriver + private readonly CustomSettingsService _customSettingsService; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + + public CustomSettingsDisplayDriver( + CustomSettingsService customSettingsService, + IContentItemDisplayManager contentItemDisplayManager) { - private readonly CustomSettingsService _customSettingsService; - private readonly IContentItemDisplayManager _contentItemDisplayManager; + _customSettingsService = customSettingsService; + _contentItemDisplayManager = contentItemDisplayManager; + } - public CustomSettingsDisplayDriver( - CustomSettingsService customSettingsService, - IContentItemDisplayManager contentItemDisplayManager) + public override async Task EditAsync(ISite site, BuildEditorContext context) + { + var contentTypeDefinition = _customSettingsService.GetSettingsType(context.GroupId); + if (contentTypeDefinition == null) { - _customSettingsService = customSettingsService; - _contentItemDisplayManager = contentItemDisplayManager; + return null; } - public override async Task EditAsync(ISite site, BuildEditorContext context) + if (!await _customSettingsService.CanUserCreateSettingsAsync(contentTypeDefinition)) { - var contentTypeDefinition = _customSettingsService.GetSettingsType(context.GroupId); - if (contentTypeDefinition == null) - { - return null; - } + return null; + } - if (!await _customSettingsService.CanUserCreateSettingsAsync(contentTypeDefinition)) - { - return null; - } + var isNew = false; + var contentItem = await _customSettingsService.GetSettingsAsync(site, contentTypeDefinition, () => isNew = true); - var isNew = false; - var contentItem = await _customSettingsService.GetSettingsAsync(site, contentTypeDefinition, () => isNew = true); + var shape = Initialize(CustomSettingsConstants.Stereotype, async ctx => + { + ctx.Editor = await _contentItemDisplayManager.BuildEditorAsync(contentItem, context.Updater, isNew); + }).Location("Content:3").OnGroup(contentTypeDefinition.Name); - var shape = Initialize(CustomSettingsConstants.Stereotype, async ctx => - { - ctx.Editor = await _contentItemDisplayManager.BuildEditorAsync(contentItem, context.Updater, isNew); - }).Location("Content:3").OnGroup(contentTypeDefinition.Name); + return shape; + } - return shape; + public override async Task UpdateAsync(ISite site, UpdateEditorContext context) + { + var contentTypeDefinition = _customSettingsService.GetSettingsType(context.GroupId); + if (contentTypeDefinition == null) + { + return null; } - public override async Task UpdateAsync(ISite site, UpdateEditorContext context) + if (!await _customSettingsService.CanUserCreateSettingsAsync(contentTypeDefinition)) { - var contentTypeDefinition = _customSettingsService.GetSettingsType(context.GroupId); - if (contentTypeDefinition == null) - { - return null; - } - - if (!await _customSettingsService.CanUserCreateSettingsAsync(contentTypeDefinition)) - { - return null; - } + return null; + } - var isNew = false; - var contentItem = await _customSettingsService.GetSettingsAsync(site, contentTypeDefinition, () => isNew = true); + var isNew = false; + var contentItem = await _customSettingsService.GetSettingsAsync(site, contentTypeDefinition, () => isNew = true); - await _contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, isNew); + await _contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, isNew); - site.Properties[contentTypeDefinition.Name] = JObject.FromObject(contentItem); + site.Properties[contentTypeDefinition.Name] = JObject.FromObject(contentItem); - return await EditAsync(site, context); - } + return await EditAsync(site, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Recipes/CustomSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Recipes/CustomSettingsStep.cs index e00e7ad6699..10806af6d1b 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Recipes/CustomSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Recipes/CustomSettingsStep.cs @@ -4,41 +4,40 @@ using OrchardCore.Recipes.Services; using OrchardCore.Settings; -namespace OrchardCore.CustomSettings.Recipes +namespace OrchardCore.CustomSettings.Recipes; + +/// +/// This recipe step updates the site settings. +/// +public sealed class CustomSettingsStep : IRecipeStepHandler { - /// - /// This recipe step updates the site settings. - /// - public sealed class CustomSettingsStep : IRecipeStepHandler + private readonly ISiteService _siteService; + + public CustomSettingsStep(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public CustomSettingsStep(ISiteService siteService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "custom-settings", StringComparison.OrdinalIgnoreCase)) { - _siteService = siteService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "custom-settings", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var siteSettings = await _siteService.LoadSiteSettingsAsync(); + var siteSettings = await _siteService.LoadSiteSettingsAsync(); - var model = context.Step; - foreach (var customSettings in model) + var model = context.Step; + foreach (var customSettings in model) + { + if (customSettings.Key == "name") { - if (customSettings.Key == "name") - { - continue; - } - - siteSettings.Properties[customSettings.Key] = customSettings.Value.DeepClone(); + continue; } - await _siteService.UpdateSiteSettingsAsync(siteSettings); + siteSettings.Properties[customSettings.Key] = customSettings.Value.DeepClone(); } + + await _siteService.UpdateSiteSettingsAsync(siteSettings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsAuthorizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsAuthorizationHandler.cs index c9a7dd0888d..baf9d148c11 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsAuthorizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsAuthorizationHandler.cs @@ -5,42 +5,41 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.CustomSettings.Services +namespace OrchardCore.CustomSettings.Services; + +public class CustomSettingsAuthorizationHandler : AuthorizationHandler { - public class CustomSettingsAuthorizationHandler : AuthorizationHandler + private readonly IServiceProvider _serviceProvider; + + public CustomSettingsAuthorizationHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public CustomSettingsAuthorizationHandler(IServiceProvider serviceProvider) + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _serviceProvider = serviceProvider; + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + if (requirement.Permission.Name != "ManageResourceSettings") + { + return; + } + + if (context.Resource == null || context.Resource.ToString() == "") + { + return; + } + + // Lazy load to prevent circular dependencies + var authorizationService = _serviceProvider.GetService(); + + if (await authorizationService.AuthorizeAsync(context.User, new Permission(Permissions.CreatePermissionName(context.Resource.ToString())))) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } - - if (requirement.Permission.Name != "ManageResourceSettings") - { - return; - } - - if (context.Resource == null || context.Resource.ToString() == "") - { - return; - } - - // Lazy load to prevent circular dependencies - var authorizationService = _serviceProvider.GetService(); - - if (await authorizationService.AuthorizeAsync(context.User, new Permission(Permissions.CreatePermissionName(context.Resource.ToString())))) - { - context.Succeed(requirement); - } + context.Succeed(requirement); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsService.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsService.cs index 04253bb2826..4e666d31c71 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Services/CustomSettingsService.cs @@ -10,120 +10,119 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Settings; -namespace OrchardCore.CustomSettings.Services +namespace OrchardCore.CustomSettings.Services; + +public class CustomSettingsService { - public class CustomSettingsService + private readonly ISiteService _siteService; + private readonly IContentManager _contentManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly Lazy>> _settingsTypes; + + public CustomSettingsService( + ISiteService siteService, + IContentManager contentManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IContentDefinitionManager contentDefinitionManager) { - private readonly ISiteService _siteService; - private readonly IContentManager _contentManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly Lazy>> _settingsTypes; - - public CustomSettingsService( - ISiteService siteService, - IContentManager contentManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IContentDefinitionManager contentDefinitionManager) - { - _siteService = siteService; - _contentManager = contentManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _contentDefinitionManager = contentDefinitionManager; - _settingsTypes = new Lazy>>(GetContentTypeAsync); - } + _siteService = siteService; + _contentManager = contentManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _contentDefinitionManager = contentDefinitionManager; + _settingsTypes = new Lazy>>(GetContentTypeAsync); + } - public async Task> GetAllSettingsTypeNamesAsync() - => (await _settingsTypes.Value).Keys; + public async Task> GetAllSettingsTypeNamesAsync() + => (await _settingsTypes.Value).Keys; - public async Task> GetAllSettingsTypesAsync() - => (await _settingsTypes.Value).Values; + public async Task> GetAllSettingsTypesAsync() + => (await _settingsTypes.Value).Values; - public async Task> GetSettingsTypesAsync(params string[] settingsTypeNames) - { - var types = await _settingsTypes.Value; - var definitions = new List(); + public async Task> GetSettingsTypesAsync(params string[] settingsTypeNames) + { + var types = await _settingsTypes.Value; + var definitions = new List(); - foreach (var settingsTypeName in settingsTypeNames) + foreach (var settingsTypeName in settingsTypeNames) + { + ContentTypeDefinition settingsType; + if (types.TryGetValue(settingsTypeName, out settingsType)) { - ContentTypeDefinition settingsType; - if (types.TryGetValue(settingsTypeName, out settingsType)) - { - definitions.Add(settingsType); - } + definitions.Add(settingsType); } - - return definitions; } - public ContentTypeDefinition GetSettingsType(string settingsTypeName) - => GetSettingsTypeAsync(settingsTypeName).Result; + return definitions; + } - public async Task GetSettingsTypeAsync(string settingsTypeName) - { - (await _settingsTypes.Value).TryGetValue(settingsTypeName, out var settingsType); + public ContentTypeDefinition GetSettingsType(string settingsTypeName) + => GetSettingsTypeAsync(settingsTypeName).Result; - return settingsType; - } + public async Task GetSettingsTypeAsync(string settingsTypeName) + { + (await _settingsTypes.Value).TryGetValue(settingsTypeName, out var settingsType); - public Task CanUserCreateSettingsAsync(ContentTypeDefinition settingsType) - { - var user = _httpContextAccessor.HttpContext?.User; + return settingsType; + } - return _authorizationService.AuthorizeAsync(user, Permissions.CreatePermissionForType(settingsType)); - } + public Task CanUserCreateSettingsAsync(ContentTypeDefinition settingsType) + { + var user = _httpContextAccessor.HttpContext?.User; - public Task GetSettingsAsync(string settingsTypeName, Action isNew = null) - { - var settingsType = GetSettingsType(settingsTypeName); - if (settingsType == null) - { - return Task.FromResult(null); - } + return _authorizationService.AuthorizeAsync(user, Permissions.CreatePermissionForType(settingsType)); + } - return GetSettingsAsync(settingsType, isNew); + public Task GetSettingsAsync(string settingsTypeName, Action isNew = null) + { + var settingsType = GetSettingsType(settingsTypeName); + if (settingsType == null) + { + return Task.FromResult(null); } - public async Task GetSettingsAsync(ContentTypeDefinition settingsType, Action isNew = null) - { - var site = await _siteService.GetSiteSettingsAsync(); + return GetSettingsAsync(settingsType, isNew); + } - return await GetSettingsAsync(site, settingsType, isNew); - } + public async Task GetSettingsAsync(ContentTypeDefinition settingsType, Action isNew = null) + { + var site = await _siteService.GetSiteSettingsAsync(); - public async Task GetSettingsAsync(ISite site, ContentTypeDefinition settingsType, Action isNew = null) - { - JsonNode property; - ContentItem contentItem; + return await GetSettingsAsync(site, settingsType, isNew); + } - if (site.Properties.TryGetPropertyValue(settingsType.Name, out property)) - { - var existing = property.ToObject(); + public async Task GetSettingsAsync(ISite site, ContentTypeDefinition settingsType, Action isNew = null) + { + JsonNode property; + ContentItem contentItem; - // Create a new item to take into account the current type definition. - contentItem = await _contentManager.NewAsync(existing.ContentType); - contentItem.Merge(existing); - } - else - { - contentItem = await _contentManager.NewAsync(settingsType.Name); - isNew?.Invoke(); - } + if (site.Properties.TryGetPropertyValue(settingsType.Name, out property)) + { + var existing = property.ToObject(); - return contentItem; + // Create a new item to take into account the current type definition. + contentItem = await _contentManager.NewAsync(existing.ContentType); + contentItem.Merge(existing); } - - private async Task> GetContentTypeAsync() + else { - var contentTypes = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + contentItem = await _contentManager.NewAsync(settingsType.Name); + isNew?.Invoke(); + } - var result = contentTypes.Where(x => x.StereotypeEquals(CustomSettingsConstants.Stereotype)) - .ToDictionary(x => x.Name); + return contentItem; + } - return result; - } + private async Task> GetContentTypeAsync() + { + var contentTypes = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + + var result = contentTypes.Where(x => x.StereotypeEquals(CustomSettingsConstants.Stereotype)) + .ToDictionary(x => x.Name); + + return result; } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Startup.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Startup.cs index 842fee1e316..93a212611e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/Startup.cs @@ -14,41 +14,40 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.CustomSettings +namespace OrchardCore.CustomSettings; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, CustomSettingsDisplayDriver>(); - services.AddScoped(); - services.AddScoped(); - // Permissions - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped, CustomSettingsDisplayDriver>(); + services.AddScoped(); + services.AddScoped(); + // Permissions + services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.Configure(options => + services.Configure(options => + { + options.Stereotypes.TryAdd("CustomSettings", new ContentTypeDefinitionDriverOptions { - options.Stereotypes.TryAdd("CustomSettings", new ContentTypeDefinitionDriverOptions - { - ShowCreatable = false, - ShowListable = false, - ShowDraftable = false, - ShowVersionable = false, - }); + ShowCreatable = false, + ShowListable = false, + ShowDraftable = false, + ShowVersionable = false, }); - } + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsDeploymentStepViewModel.cs index e177b86f0c1..4863f97912c 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.CustomSettings.ViewModels +namespace OrchardCore.CustomSettings.ViewModels; + +public class CustomSettingsDeploymentStepViewModel { - public class CustomSettingsDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] SettingsTypeNames { get; set; } - public string[] AllSettingsTypeNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] SettingsTypeNames { get; set; } + public string[] AllSettingsTypeNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsEditViewModel.cs index ba24da44c4c..a4db4913d16 100644 --- a/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.CustomSettings/ViewModels/CustomSettingsEditViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.CustomSettings.ViewModels +namespace OrchardCore.CustomSettings.ViewModels; + +public class CustomSettingsEditViewModel { - public class CustomSettingsEditViewModel - { - public dynamic Editor { get; set; } - } + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Demo/AdminMenu.cs index 6b6c6773cb5..bdaab0eb52e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/AdminMenu.cs @@ -2,56 +2,55 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Demo +namespace OrchardCore.Demo; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Demo"], "10", demo => demo - .AddClass("demo").Id("demo") - .Add(S["This Menu Item 1"], "0", item => item - .Add(S["This is Menu Item 1.1"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - .Add(S["This is Menu Item 1.2"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - .Add(S["This is Menu Item 1.2"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - ) - .Add(S["This Menu Item 2"], "0", item => item - .Add(S["This is Menu Item 2.1"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - .Add(S["This is Menu Item 2.2"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - .Add(S["This is Menu Item 3.2"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - ) - .Add(S["This Menu Item 3"], "0", item => item - .Add(S["This is Menu Item 3.1"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) - .Add(S["This is Menu Item 3.2"], subItem => subItem - .Action("Index", "Admin", "OrchardCore.Demo")) + builder + .Add(S["Demo"], "10", demo => demo + .AddClass("demo").Id("demo") + .Add(S["This Menu Item 1"], "0", item => item + .Add(S["This is Menu Item 1.1"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + .Add(S["This is Menu Item 1.2"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + .Add(S["This is Menu Item 1.2"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + ) + .Add(S["This Menu Item 2"], "0", item => item + .Add(S["This is Menu Item 2.1"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + .Add(S["This is Menu Item 2.2"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + .Add(S["This is Menu Item 3.2"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + ) + .Add(S["This Menu Item 3"], "0", item => item + .Add(S["This is Menu Item 3.1"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) + .Add(S["This is Menu Item 3.2"], subItem => subItem + .Action("Index", "Admin", "OrchardCore.Demo")) - ) - .Add(S["Todo (Liquid - Frontend)"], "0", item => item - .Action("Index", "Todo", "OrchardCore.Demo") - ) - ); + ) + .Add(S["Todo (Liquid - Frontend)"], "0", item => item + .Action("Index", "Todo", "OrchardCore.Demo") + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Commands/DemoCommands.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Commands/DemoCommands.cs index d21a3f03584..76c0f127ad4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Commands/DemoCommands.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Commands/DemoCommands.cs @@ -2,26 +2,25 @@ using Microsoft.Extensions.Logging; using OrchardCore.Environment.Commands; -namespace OrchardCore.Demo.Commands +namespace OrchardCore.Demo.Commands; + +public class DemoCommands : DefaultCommandHandler { - public class DemoCommands : DefaultCommandHandler - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public DemoCommands(ILogger logger, - IStringLocalizer localizer) : base(localizer) - { - _logger = logger; - } + public DemoCommands(ILogger logger, + IStringLocalizer localizer) : base(localizer) + { + _logger = logger; + } - [CommandName("demo helloworld")] - [CommandHelp("demo helloworld says hi!")] - public void HelloWorld() + [CommandName("demo helloworld")] + [CommandHelp("demo helloworld says hi!")] + public void HelloWorld() + { + if (_logger.IsEnabled(LogLevel.Information)) { - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("Hi there from Hello World!"); - } + _logger.LogInformation("Hi there from Hello World!"); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Components/FakeViewComponent.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Components/FakeViewComponent.cs index d6184ee3e7f..ae250361a3b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Components/FakeViewComponent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Components/FakeViewComponent.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.Demo.Components +namespace OrchardCore.Demo.Components; + +public class FakeViewComponent : ViewComponent { - public class FakeViewComponent : ViewComponent + public IViewComponentResult Invoke(string value) { - public IViewComponentResult Invoke(string value) - { - return View("Default", value); - } + return View("Default", value); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/ContentElementDisplays/TestContentElementDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Demo/ContentElementDisplays/TestContentElementDisplayDriver.cs index 97300728c47..d0d276b36ed 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/ContentElementDisplays/TestContentElementDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/ContentElementDisplays/TestContentElementDisplayDriver.cs @@ -7,81 +7,80 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Demo.ContentElementDisplays +namespace OrchardCore.Demo.ContentElementDisplays; + +public sealed class TestContentElementDisplayDriver : ContentDisplayDriver { - public sealed class TestContentElementDisplayDriver : ContentDisplayDriver + private static int _creating; + private static int _processing; + + public override IDisplayResult Display(ContentItem contentItem, BuildDisplayContext context) { - private static int _creating; - private static int _processing; + var testContentPart = contentItem.As(); - public override IDisplayResult Display(ContentItem contentItem, BuildDisplayContext context) + if (testContentPart == null) { - var testContentPart = contentItem.As(); + return null; + } - if (testContentPart == null) - { - return null; - } + return Combine( + // A new shape is created and the properties of the object are bound to it when rendered + Copy("TestContentPartA", testContentPart).Location("Detail", "Content"), + // New shape, no initialization, custom location + Dynamic("LowerDoll").Location("Detail", "Footer"), + // New shape + Factory("TestContentPartA", + async ctx => (await ctx.New.TestContentPartA()).Creating(_creating++), + shape => + { + shape.Properties["Processing"] = _processing++; - return Combine( - // A new shape is created and the properties of the object are bound to it when rendered - Copy("TestContentPartA", testContentPart).Location("Detail", "Content"), - // New shape, no initialization, custom location - Dynamic("LowerDoll").Location("Detail", "Footer"), - // New shape - Factory("TestContentPartA", - async ctx => (await ctx.New.TestContentPartA()).Creating(_creating++), - shape => - { - shape.Properties["Processing"] = _processing++; + return Task.CompletedTask; + }) + .Location("Detail", "Content") + .Cache("lowerdoll2", cache => cache.WithExpiryAfter(TimeSpan.FromSeconds(5))), + // A strongly typed shape model is used and initialized when rendered + Initialize(shape => { shape.Line = "Strongly typed shape"; }) + .Location("Detail", "Content:2"), + // Cached shape + Dynamic("LowerDoll") + .Location("Detail", "/Footer") + .Cache("lowerdoll", cache => cache.WithExpiryAfter(TimeSpan.FromSeconds(5))) + ); + } - return Task.CompletedTask; - }) - .Location("Detail", "Content") - .Cache("lowerdoll2", cache => cache.WithExpiryAfter(TimeSpan.FromSeconds(5))), - // A strongly typed shape model is used and initialized when rendered - Initialize(shape => { shape.Line = "Strongly typed shape"; }) - .Location("Detail", "Content:2"), - // Cached shape - Dynamic("LowerDoll") - .Location("Detail", "/Footer") - .Cache("lowerdoll", cache => cache.WithExpiryAfter(TimeSpan.FromSeconds(5))) - ); - } + public override IDisplayResult Edit(ContentItem contentItem, BuildEditorContext context) + { + var testContentPart = contentItem.As(); - public override IDisplayResult Edit(ContentItem contentItem, BuildEditorContext context) + if (testContentPart == null) { - var testContentPart = contentItem.As(); - - if (testContentPart == null) - { - return null; - } - - return Copy("TestContentPartA_Edit", testContentPart).Location("Content"); + return null; } - public override async Task UpdateAsync(ContentItem contentItem, UpdateEditorContext context) - { - var testContentPart = contentItem.As(); + return Copy("TestContentPartA_Edit", testContentPart).Location("Content"); + } - if (testContentPart == null) - { - return null; - } + public override async Task UpdateAsync(ContentItem contentItem, UpdateEditorContext context) + { + var testContentPart = contentItem.As(); - await context.Updater.TryUpdateModelAsync(testContentPart, ""); + if (testContentPart == null) + { + return null; + } - if (testContentPart.Line.EndsWith(' ')) - { - context.Updater.ModelState.AddModelError(nameof(testContentPart.Line), "Value cannot end with a space"); - } - else - { - contentItem.Apply(testContentPart); - } + await context.Updater.TryUpdateModelAsync(testContentPart, ""); - return Copy("TestContentPartA_Edit", testContentPart).Location("Content"); + if (testContentPart.Line.EndsWith(' ')) + { + context.Updater.ModelState.AddModelError(nameof(testContentPart.Line), "Value cannot end with a space"); + } + else + { + contentItem.Apply(testContentPart); } + + return Copy("TestContentPartA_Edit", testContentPart).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/AdminController.cs index 29ae2eb7222..298b42587df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/AdminController.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + public IActionResult Index() { - public IActionResult Index() - { - return View(); - } + return View(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentApiController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentApiController.cs index 098db090c55..02c07f60fca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentApiController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentApiController.cs @@ -4,68 +4,67 @@ using OrchardCore.ContentManagement; using OrchardCore.Contents; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +[Route("api/demo")] +[Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] +[ApiController] +public class ContentApiController : ControllerBase { - [Route("api/demo")] - [Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] - [ApiController] - public class ContentApiController : ControllerBase + private readonly IAuthorizationService _authorizationService; + private readonly IContentManager _contentManager; + + public ContentApiController(IAuthorizationService authorizationService, IContentManager contentManager) + { + _authorizationService = authorizationService; + _contentManager = contentManager; + } + + public async Task GetById(string id) { - private readonly IAuthorizationService _authorizationService; - private readonly IContentManager _contentManager; + var contentItem = await _contentManager.GetAsync(id); - public ContentApiController(IAuthorizationService authorizationService, IContentManager contentManager) + if (contentItem == null) { - _authorizationService = authorizationService; - _contentManager = contentManager; + return NotFound(); } - public async Task GetById(string id) + return new ObjectResult(contentItem); + } + + public async Task GetAuthorizedById(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.DemoAPIAccess)) { - var contentItem = await _contentManager.GetAsync(id); + return this.ChallengeOrForbid("Api"); + } - if (contentItem == null) - { - return NotFound(); - } + var contentItem = await _contentManager.GetAsync(id); - return new ObjectResult(contentItem); + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, contentItem)) + { + return this.ChallengeOrForbid("Api"); } - public async Task GetAuthorizedById(string id) + if (contentItem == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.DemoAPIAccess)) - { - return this.ChallengeOrForbid("Api"); - } - - var contentItem = await _contentManager.GetAsync(id); - - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ViewContent, contentItem)) - { - return this.ChallengeOrForbid("Api"); - } - - if (contentItem == null) - { - return NotFound(); - } - - return new ObjectResult(contentItem); + return NotFound(); } - [HttpPost] - [Authorize] - public async Task AddContent(ContentItem contentItem) + return new ObjectResult(contentItem); + } + + [HttpPost] + [Authorize] + public async Task AddContent(ContentItem contentItem) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.DemoAPIAccess)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.DemoAPIAccess)) - { - return this.ChallengeOrForbid("Api"); - } + return this.ChallengeOrForbid("Api"); + } - await _contentManager.CreateAsync(contentItem); + await _contentManager.CreateAsync(contentItem); - return new ObjectResult(contentItem); - } + return new ObjectResult(contentItem); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs index d51165bdfb8..4f4a4ad2c6a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/ContentController.cs @@ -9,95 +9,94 @@ using YesSql; using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +public class ContentController : Controller { - public class ContentController : Controller + private readonly IContentItemDisplayManager _contentDisplay; + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + public ContentController( + IContentManager contentManager, + IContentItemDisplayManager contentDisplay, + ISession session, + IUpdateModelAccessor updateModelAccessor, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IContentItemDisplayManager _contentDisplay; - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; - - public ContentController( - IContentManager contentManager, - IContentItemDisplayManager contentDisplay, - ISession session, - IUpdateModelAccessor updateModelAccessor, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + _contentManager = contentManager; + _contentDisplay = contentDisplay; + _session = session; + _updateModelAccessor = updateModelAccessor; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } + + public async Task Display(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId); + + if (contentItem == null) { - _contentManager = contentManager; - _contentDisplay = contentDisplay; - _session = session; - _updateModelAccessor = updateModelAccessor; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; + return NotFound(); } - public async Task Display(string contentItemId) + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId); + return Forbid(); + } - if (contentItem == null) - { - return NotFound(); - } + var shape = await _contentDisplay.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater); + return View(shape); + } - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.ViewContent, contentItem)) - { - return Forbid(); - } + [Admin("Demo/Content/Edit", "Demo.Content.Edit")] + public async Task Edit(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId); - var shape = await _contentDisplay.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater); - return View(shape); + if (contentItem == null) + { + return NotFound(); } - [Admin("Demo/Content/Edit", "Demo.Content.Edit")] - public async Task Edit(string contentItemId) + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId); + return Forbid(); + } - if (contentItem == null) - { - return NotFound(); - } + var shape = await _contentDisplay.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + return View(shape); + } - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } + [Admin, HttpPost, ActionName("Edit")] + public async Task EditPost(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId); - var shape = await _contentDisplay.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - return View(shape); + if (contentItem == null) + { + return NotFound(); } - [Admin, HttpPost, ActionName("Edit")] - public async Task EditPost(string contentItemId) + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) { - var contentItem = await _contentManager.GetAsync(contentItemId); - - if (contentItem == null) - { - return NotFound(); - } - - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, CommonPermissions.EditContent, contentItem)) - { - return Forbid(); - } - - var shape = await _contentDisplay.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + return Forbid(); + } - if (!ModelState.IsValid) - { - await _session.CancelAsync(); - return View(nameof(Edit), shape); - } + var shape = await _contentDisplay.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - await _session.SaveAsync(contentItem); - return RedirectToAction(nameof(Edit), contentItemId); + if (!ModelState.IsValid) + { + await _session.CancelAsync(); + return View(nameof(Edit), shape); } + + await _session.SaveAsync(contentItem); + return RedirectToAction(nameof(Edit), contentItemId); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/DemoController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/DemoController.cs index ed9290f6745..6ffe1b85a11 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/DemoController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/DemoController.cs @@ -1,24 +1,23 @@ using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +public class DemoController : Controller { - public class DemoController : Controller + [Route("Demo")] + [Route("Demo/Index")] + public IActionResult Index() + { + return Content("Index content"); + } + [Route("Demo/About")] + public IActionResult About() + { + return Content("About content"); + } + [Route("Demo/Contact")] + public IActionResult Contact() { - [Route("Demo")] - [Route("Demo/Index")] - public IActionResult Index() - { - return Content("Index content"); - } - [Route("Demo/About")] - public IActionResult About() - { - return Content("About content"); - } - [Route("Demo/Contact")] - public IActionResult Contact() - { - return Content("Contact content"); - } + return Content("Contact content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs index 69ce76ff4b4..2ca96f47b54 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/HomeController.cs @@ -12,143 +12,142 @@ using OrchardCore.Environment.Shell.Scope; using YesSql; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +public class HomeController : Controller { - public class HomeController : Controller + private readonly ITestDependency _testDependency; + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly ILogger _logger; + private readonly ITagCache _tagCache; + private readonly IContentItemDisplayManager _contentDisplay; + + public HomeController( + ITestDependency testDependency, + IContentManager contentManager, + IShapeFactory shapeFactory, + ISession session, + ILogger logger, + ITagCache tagCache, + IContentItemDisplayManager contentDisplay) { - private readonly ITestDependency _testDependency; - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly ILogger _logger; - private readonly ITagCache _tagCache; - private readonly IContentItemDisplayManager _contentDisplay; - - public HomeController( - ITestDependency testDependency, - IContentManager contentManager, - IShapeFactory shapeFactory, - ISession session, - ILogger logger, - ITagCache tagCache, - IContentItemDisplayManager contentDisplay) - { - _session = session; - _testDependency = testDependency; - _contentManager = contentManager; - Shape = shapeFactory; - _logger = logger; - _tagCache = tagCache; - _contentDisplay = contentDisplay; - } + _session = session; + _testDependency = testDependency; + _contentManager = contentManager; + Shape = shapeFactory; + _logger = logger; + _tagCache = tagCache; + _contentDisplay = contentDisplay; + } - private dynamic Shape { get; set; } + private dynamic Shape { get; set; } - public ActionResult Index() - { - return View(); - } + public ActionResult Index() + { + return View(); + } - [HttpPost] - public async Task Index(string text) - { - var contentItem = await _contentManager.NewAsync("Foo"); + [HttpPost] + public async Task Index(string text) + { + var contentItem = await _contentManager.NewAsync("Foo"); - // Dynamic syntax - contentItem.Content.TestContentPartA.Line = text + "blah"; + // Dynamic syntax + contentItem.Content.TestContentPartA.Line = text + "blah"; - // Explicit syntax - var testPart = contentItem.As(); - testPart.Line = text; - contentItem.Apply(testPart); + // Explicit syntax + var testPart = contentItem.As(); + testPart.Line = text; + contentItem.Apply(testPart); - // "Alter" syntax - contentItem.Alter(x => x.Line = text); + // "Alter" syntax + contentItem.Alter(x => x.Line = text); - await _contentManager.CreateAsync(contentItem); + await _contentManager.CreateAsync(contentItem); - _logger.LogInformation("This is some log"); + _logger.LogInformation("This is some log"); - return RedirectToAction("Display", "Home", new { area = "OrchardCore.Demo", contentItemId = contentItem.ContentItemId }); - } + return RedirectToAction("Display", "Home", new { area = "OrchardCore.Demo", contentItemId = contentItem.ContentItemId }); + } - public ActionResult Tag() - { - return View(); - } + public ActionResult Tag() + { + return View(); + } - [HttpPost] - public async Task Tag(string tag) - { - await _tagCache.RemoveTagAsync(tag); - return RedirectToAction(nameof(Tag), "Home", new { area = "OrchardCore.Demo" }); - } + [HttpPost] + public async Task Tag(string tag) + { + await _tagCache.RemoveTagAsync(tag); + return RedirectToAction(nameof(Tag), "Home", new { area = "OrchardCore.Demo" }); + } - public async Task Display(string contentItemId) + public async Task Display(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId); + + if (contentItem == null) { - var contentItem = await _contentManager.GetAsync(contentItemId); + return NotFound(); + } - if (contentItem == null) - { - return NotFound(); - } + return View("Display", contentItem); + } - return View("Display", contentItem); - } + public async Task DisplayShape(string contentItemId) + { + var contentItem = await _contentManager.GetAsync(contentItemId); - public async Task DisplayShape(string contentItemId) + if (contentItem == null) { - var contentItem = await _contentManager.GetAsync(contentItemId); - - if (contentItem == null) - { - return NotFound(); - } + return NotFound(); + } - var shape = await Shape.Foo(Line: contentItem.As().Line); + var shape = await Shape.Foo(Line: contentItem.As().Line); - return View(shape); - } + return View(shape); + } - public IActionResult AddProperty() => View(); + public IActionResult AddProperty() => View(); - public ActionResult Raw() - { - return View(); - } + public ActionResult Raw() + { + return View(); + } - public ActionResult Cache() - { - return View(); - } + public ActionResult Cache() + { + return View(); + } #pragma warning disable CA1822 // Mark members as static - public string GCCollect() - { - GC.Collect(); - return "OK"; - } + public string GCCollect() + { + GC.Collect(); + return "OK"; + } - public ActionResult IndexError() - { - throw new Exception("ERROR!!!!"); - } + public ActionResult IndexError() + { + throw new Exception("ERROR!!!!"); + } - public string CreateTask() + public string CreateTask() + { + ShellScope.AddDeferredTask(scope => { - ShellScope.AddDeferredTask(scope => - { - var logger = scope.ServiceProvider.GetService>(); - logger.LogError("Task deferred successfully"); - return Task.CompletedTask; - }); - - return "Check for logs"; - } + var logger = scope.ServiceProvider.GetService>(); + logger.LogError("Task deferred successfully"); + return Task.CompletedTask; + }); + + return "Check for logs"; + } #pragma warning restore CA1822 // Mark members as static - public IActionResult ShapePerformance() - { - return View(); - } + public IActionResult ShapePerformance() + { + return View(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/TodoController.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/TodoController.cs index 33f7c16652f..1c0f9ce6637 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/TodoController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Controllers/TodoController.cs @@ -5,108 +5,107 @@ using OrchardCore.Demo.ViewModels; using YesSql; -namespace OrchardCore.Demo.Controllers +namespace OrchardCore.Demo.Controllers; + +public class TodoController : Controller { - public class TodoController : Controller + private readonly ISession _session; + private readonly Entities.IIdGenerator _idGenerator; + + public TodoController(ISession session, Entities.IIdGenerator idGenerator) { - private readonly ISession _session; - private readonly Entities.IIdGenerator _idGenerator; + _session = session; + _idGenerator = idGenerator; + } - public TodoController(ISession session, Entities.IIdGenerator idGenerator) - { - _session = session; - _idGenerator = idGenerator; - } + public async Task Index() + { + var list = (await _session.Query().ListAsync()) + .Select(m => new TodoViewModel() + { + TodoId = m.TodoId, + Text = m.Text, + DueDate = m.DueDate, + IsCompleted = m.IsCompleted, + }) + .ToList(); + + return View(list); + } - public async Task Index() + public IActionResult Create() + { + var viewModel = new TodoViewModel { - var list = (await _session.Query().ListAsync()) - .Select(m => new TodoViewModel() - { - TodoId = m.TodoId, - Text = m.Text, - DueDate = m.DueDate, - IsCompleted = m.IsCompleted, - }) - .ToList(); - - return View(list); - } + TodoId = _idGenerator.GenerateUniqueId(), + DisplayMode = "Edit", + }; - public IActionResult Create() - { - var viewModel = new TodoViewModel - { - TodoId = _idGenerator.GenerateUniqueId(), - DisplayMode = "Edit", - }; + return View(nameof(Edit), viewModel); + } - return View(nameof(Edit), viewModel); - } + public async Task Edit(string todoId) + { + var model = (await _session.Query().ListAsync()) + .FirstOrDefault(m => m.TodoId == todoId); - public async Task Edit(string todoId) + if (model == null) { - var model = (await _session.Query().ListAsync()) - .FirstOrDefault(m => m.TodoId == todoId); - - if (model == null) - { - return NotFound(); - } - - var viewModel = new TodoViewModel() - { - TodoId = model.TodoId, - Text = model.Text, - DueDate = model.DueDate, - IsCompleted = model.IsCompleted, - DisplayMode = "Edit", - }; - - return View(viewModel); + return NotFound(); } - [HttpPost] - public async Task Update(TodoViewModel viewModel, string returnUrl = "") + var viewModel = new TodoViewModel() { - if (ModelState.IsValid) - { - var model = (await _session.Query().ListAsync()) - .FirstOrDefault(m => m.TodoId == viewModel.TodoId); + TodoId = model.TodoId, + Text = model.Text, + DueDate = model.DueDate, + IsCompleted = model.IsCompleted, + DisplayMode = "Edit", + }; + + return View(viewModel); + } - model ??= new TodoModel() { TodoId = viewModel.TodoId }; + [HttpPost] + public async Task Update(TodoViewModel viewModel, string returnUrl = "") + { + if (ModelState.IsValid) + { + var model = (await _session.Query().ListAsync()) + .FirstOrDefault(m => m.TodoId == viewModel.TodoId); - model.Text = viewModel.Text; - model.DueDate = viewModel.DueDate; - model.IsCompleted = viewModel.IsCompleted; + model ??= new TodoModel() { TodoId = viewModel.TodoId }; - await _session.SaveAsync(model); + model.Text = viewModel.Text; + model.DueDate = viewModel.DueDate; + model.IsCompleted = viewModel.IsCompleted; - if (Url.IsLocalUrl(returnUrl)) - { - return this.Redirect(returnUrl, true); - } + await _session.SaveAsync(model); - return RedirectToAction(nameof(Index), "Todo"); + if (Url.IsLocalUrl(returnUrl)) + { + return this.Redirect(returnUrl, true); } - viewModel.DisplayMode = "Edit"; - return View(nameof(Edit), viewModel); + return RedirectToAction(nameof(Index), "Todo"); } - public async Task Delete(string todoId) - { - var model = (await _session.Query().ListAsync()) - .FirstOrDefault(m => m.TodoId == todoId); - - if (model == null) - { - return NotFound(); - } + viewModel.DisplayMode = "Edit"; + return View(nameof(Edit), viewModel); + } - _session.Delete(model); + public async Task Delete(string todoId) + { + var model = (await _session.Query().ListAsync()) + .FirstOrDefault(m => m.TodoId == todoId); - return RedirectToAction(nameof(Index), "Todo"); + if (model == null) + { + return NotFound(); } + + _session.Delete(model); + + return RedirectToAction(nameof(Index), "Todo"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Drivers/UserProfileDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Drivers/UserProfileDisplayDriver.cs index 6691f8f80cc..eb0bac32944 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Drivers/UserProfileDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Drivers/UserProfileDisplayDriver.cs @@ -9,48 +9,47 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Users.Models; -namespace OrchardCore.Demo.Drivers +namespace OrchardCore.Demo.Drivers; + +public sealed class UserProfileDisplayDriver : SectionDisplayDriver { - public sealed class UserProfileDisplayDriver : SectionDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public UserProfileDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public UserProfileDisplayDriver(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService) + public override IDisplayResult Edit(User user, UserProfile profile, BuildEditorContext context) + { + return Initialize("UserProfile_Edit", model => { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + model.Age = profile.Age; + model.FirstName = profile.FirstName; + model.LastName = profile.LastName; + }) + .Location("Content:2") + .RenderWhen(() => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageOwnUserProfile)); + } - public override IDisplayResult Edit(User user, UserProfile profile, BuildEditorContext context) + public override async Task UpdateAsync(User user, UserProfile profile, UpdateEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageOwnUserProfile)) { - return Initialize("UserProfile_Edit", model => - { - model.Age = profile.Age; - model.FirstName = profile.FirstName; - model.LastName = profile.LastName; - }) - .Location("Content:2") - .RenderWhen(() => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageOwnUserProfile)); + return Edit(user, profile, context); } - public override async Task UpdateAsync(User user, UserProfile profile, UpdateEditorContext context) - { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageOwnUserProfile)) - { - return Edit(user, profile, context); - } - - var model = new EditUserProfileViewModel(); + var model = new EditUserProfileViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - profile.Age = model.Age; - profile.FirstName = model.FirstName; - profile.LastName = model.LastName; - profile.UpdatedAt = DateTime.UtcNow; + profile.Age = model.Age; + profile.FirstName = model.FirstName; + profile.LastName = model.LastName; + profile.UpdatedAt = DateTime.UtcNow; - return Edit(user, profile, context); - } + return Edit(user, profile, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/Startup.cs index c4d596b58f9..208ed08e34e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/Startup.cs @@ -4,81 +4,44 @@ using OrchardCore.Demo.Models; using OrchardCore.Modules; -namespace OrchardCore.Demo.GraphQL +namespace OrchardCore.Demo.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(options => { - services.Configure(options => - { - // Top Level Content Type options - options.ContentTypeOptions = options.ContentTypeOptions.Union(new[] { - new GraphQLContentTypeOption("Blog") - { - Collapse = false, - Hidden = false, - PartOptions = new GraphQLContentPartOption[] { - // Content Part options attached to Content Type - new("TestContentPartA") - { - Collapse = false, - Hidden = false - }, - new GraphQLContentPartOption - { - Collapse = false, - Hidden = false - } + // Top Level Content Type options + options.ContentTypeOptions = options.ContentTypeOptions.Union(new[] { + new GraphQLContentTypeOption("Blog") + { + Collapse = false, + Hidden = false, + PartOptions = new GraphQLContentPartOption[] { + // Content Part options attached to Content Type + new("TestContentPartA") + { + Collapse = false, + Hidden = false + }, + new GraphQLContentPartOption + { + Collapse = false, + Hidden = false } } } - ); - - options.ConfigureContentType("Blog", (typeConfig) => - { - typeConfig.Collapse = false; - typeConfig.Hidden = false; - - typeConfig - .ConfigurePart("TestContentPartA", (partConfig) => - { - partConfig.Collapse = false; - partConfig.Hidden = false; - }) - .ConfigurePart((partConfig) => - { - partConfig.Collapse = false; - partConfig.Hidden = false; - }); - }); + } + ); - // Ignore Fields on GraphQL Objects - options.HiddenFields = options.HiddenFields.Union(new[] { - new GraphQLField("lineIgnored"), - new GraphQLField("lineOtherIgnored") - }); - - options - .IgnoreField("lineIgnored") - .IgnoreField("lineOtherIgnored"); - - // Top level Part Options - options.PartOptions = options.PartOptions.Union(new[] { - new GraphQLContentPartOption("TestContentPartA") - { - Collapse = false, - Hidden = false - }, - new GraphQLContentPartOption - { - Collapse = false, - Hidden = false - } - }); + options.ConfigureContentType("Blog", (typeConfig) => + { + typeConfig.Collapse = false; + typeConfig.Hidden = false; - options + typeConfig .ConfigurePart("TestContentPartA", (partConfig) => { partConfig.Collapse = false; @@ -90,6 +53,42 @@ public override void ConfigureServices(IServiceCollection services) partConfig.Hidden = false; }); }); - } + + // Ignore Fields on GraphQL Objects + options.HiddenFields = options.HiddenFields.Union(new[] { + new GraphQLField("lineIgnored"), + new GraphQLField("lineOtherIgnored") + }); + + options + .IgnoreField("lineIgnored") + .IgnoreField("lineOtherIgnored"); + + // Top level Part Options + options.PartOptions = options.PartOptions.Union(new[] { + new GraphQLContentPartOption("TestContentPartA") + { + Collapse = false, + Hidden = false + }, + new GraphQLContentPartOption + { + Collapse = false, + Hidden = false + } + }); + + options + .ConfigurePart("TestContentPartA", (partConfig) => + { + partConfig.Collapse = false; + partConfig.Hidden = false; + }) + .ConfigurePart((partConfig) => + { + partConfig.Collapse = false; + partConfig.Hidden = false; + }); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/TestQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/TestQueryObjectType.cs index 982345de955..1e87829b396 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/TestQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/GraphQL/TestQueryObjectType.cs @@ -1,17 +1,16 @@ using GraphQL.Types; using OrchardCore.Demo.Models; -namespace OrchardCore.Demo.GraphQL +namespace OrchardCore.Demo.GraphQL; + +public class TestQueryObjectType : ObjectGraphType { - public class TestQueryObjectType : ObjectGraphType + public TestQueryObjectType() { - public TestQueryObjectType() - { - Name = "TestContentPartA"; + Name = "TestContentPartA"; - Field("line", x => x.Line, true); - Field("lineIgnored", x => x.Line, true); - Field("lineOtherIgnored", x => x.Line, true); - } + Field("line", x => x.Line, true); + Field("lineIgnored", x => x.Line, true); + Field("lineOtherIgnored", x => x.Line, true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/BlockingMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/BlockingMiddleware.cs index 790f762bef6..1e20ea8c47a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/BlockingMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/BlockingMiddleware.cs @@ -1,27 +1,26 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace OrchardCore.Demo +namespace OrchardCore.Demo; + +public class BlockingMiddleware { - public class BlockingMiddleware + private readonly RequestDelegate _next; + + public BlockingMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - public BlockingMiddleware(RequestDelegate next) + public async Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.Value == "/middleware") { - _next = next; + await httpContext.Response.WriteAsync("middleware"); } - - public async Task Invoke(HttpContext httpContext) + else { - if (httpContext.Request.Path.Value == "/middleware") - { - await httpContext.Response.WriteAsync("middleware"); - } - else - { - await _next.Invoke(httpContext); - } + await _next.Invoke(httpContext); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/NonBlockingMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/NonBlockingMiddleware.cs index 7905e41e368..f3836b2458a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/NonBlockingMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Middlewares/NonBlockingMiddleware.cs @@ -1,22 +1,21 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -namespace OrchardCore.Demo +namespace OrchardCore.Demo; + +public class NonBlockingMiddleware { - public class NonBlockingMiddleware - { - private readonly RequestDelegate _next; + private readonly RequestDelegate _next; - public NonBlockingMiddleware(RequestDelegate next) - { - _next = next; - } + public NonBlockingMiddleware(RequestDelegate next) + { + _next = next; + } - public async Task Invoke(HttpContext httpContext) - { - // If the tenant pipeline is re-executed on error, the header is already set. - httpContext.Response.Headers["Orchard"] = "2.0"; - await _next.Invoke(httpContext); - } + public async Task Invoke(HttpContext httpContext) + { + // If the tenant pipeline is re-executed on error, the header is already set. + httpContext.Response.Headers["Orchard"] = "2.0"; + await _next.Invoke(httpContext); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Migrations.cs index 701d1ed57e7..966e2b6f49e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Migrations.cs @@ -2,25 +2,24 @@ using OrchardCore.ContentManagement.Metadata; using OrchardCore.Data.Migration; -namespace OrchardCore.Demo +namespace OrchardCore.Demo; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterTypeDefinitionAsync("Foo", builder => builder - .WithPart("TestContentPartA") - .WithPart("TestContentPartB") - ); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterTypeDefinitionAsync("Foo", builder => builder + .WithPart("TestContentPartA") + .WithPart("TestContentPartB") + ); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/CustomViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/CustomViewModel.cs index bc94a6d7501..979a78768d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/CustomViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/CustomViewModel.cs @@ -1,9 +1,8 @@ using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class CustomViewModel : ShapeViewModel { - public class CustomViewModel : ShapeViewModel - { - public string Value { get; set; } - } + public string Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/FakeShape.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/FakeShape.cs index fff8a3b4d82..40eef18a6eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/FakeShape.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/FakeShape.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class FakeShape { - public class FakeShape - { - public string Value { get; set; } - } + public string Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/HomeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/HomeViewModel.cs index 81252e8d55e..f82863fe2fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/HomeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/HomeViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class HomeViewModel { - public class HomeViewModel - { - public string Text { get; set; } - public dynamic Foo { get; set; } - } + public string Text { get; set; } + public dynamic Foo { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentField.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentField.cs index 3889e5202fa..5dd4f51e94b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentField.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class TestContentField : ContentField { - public class TestContentField : ContentField - { - public string Text; - } + public string Text; } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartA.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartA.cs index 15140ab555a..71abfede047 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartA.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartA.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class TestContentPartA : ContentPart { - public class TestContentPartA : ContentPart - { - public ShapeMetadata Metadata { get; set; } - public string Line { get; set; } - } + public ShapeMetadata Metadata { get; set; } + public string Line { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartAShape.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartAShape.cs index 43af4e38eeb..6207ac3cc54 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartAShape.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TestContentPartAShape.cs @@ -1,9 +1,8 @@ using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class TestContentPartAShape : Shape { - public class TestContentPartAShape : Shape - { - public string Line { get; set; } - } + public string Line { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TodoModel.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TodoModel.cs index cd2385aca54..bce9d5474f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/TodoModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/TodoModel.cs @@ -1,12 +1,11 @@ using System; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class TodoModel { - public class TodoModel - { - public string TodoId { get; set; } - public string Text { get; set; } - public DateTime DueDate { get; set; } - public bool IsCompleted { get; set; } - } + public string TodoId { get; set; } + public string Text { get; set; } + public DateTime DueDate { get; set; } + public bool IsCompleted { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Models/UserProfile.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Models/UserProfile.cs index b10af471a77..49071b5c286 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Models/UserProfile.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Models/UserProfile.cs @@ -1,12 +1,11 @@ using System; -namespace OrchardCore.Demo.Models +namespace OrchardCore.Demo.Models; + +public class UserProfile { - public class UserProfile - { - public int Age { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public DateTime UpdatedAt { get; set; } - } + public int Age { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime UpdatedAt { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/Admin/Edit.cshtml.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/Admin/Edit.cshtml.cs index 0df7687364c..0d7998168c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/Admin/Edit.cshtml.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/Admin/Edit.cshtml.cs @@ -8,87 +8,86 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.Demo.Pages +namespace OrchardCore.Demo.Pages; + +[Feature("OrchardCore.Demo.Foo")] +public class EditModel : PageModel { - [Feature("OrchardCore.Demo.Foo")] - public class EditModel : PageModel + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentDisplay; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly ISession _session; + + public EditModel( + IContentManager contentManager, + IContentItemDisplayManager contentDisplay, + IUpdateModelAccessor updateModelAccessor, + ISession session) { - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentDisplay; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly ISession _session; - - public EditModel( - IContentManager contentManager, - IContentItemDisplayManager contentDisplay, - IUpdateModelAccessor updateModelAccessor, - ISession session) - { - _contentManager = contentManager; - _contentDisplay = contentDisplay; - _updateModelAccessor = updateModelAccessor; - _session = session; - } + _contentManager = contentManager; + _contentDisplay = contentDisplay; + _updateModelAccessor = updateModelAccessor; + _session = session; + } - [BindProperty(SupportsGet = true)] - public string Id { get; set; } + [BindProperty(SupportsGet = true)] + public string Id { get; set; } - [Required] - [BindProperty] - public string Text { get; set; } + [Required] + [BindProperty] + public string Text { get; set; } - public string Title { get; set; } - public string Submit { get; set; } + public string Title { get; set; } + public string Submit { get; set; } - public async Task OnGetAsync() - { - var contentItem = await _contentManager.GetAsync(Id); + public async Task OnGetAsync() + { + var contentItem = await _contentManager.GetAsync(Id); - if (contentItem == null) - { - return NotFound(); - } + if (contentItem == null) + { + return NotFound(); + } - Title = contentItem.DisplayText ?? "Foo Title"; - Text = contentItem.Content.TestContentPartA.Line; + Title = contentItem.DisplayText ?? "Foo Title"; + Text = contentItem.Content.TestContentPartA.Line; - return Page(); - } + return Page(); + } - public async Task OnPostDelete() + public async Task OnPostDelete() + { + var contentItem = await _contentManager.GetAsync(Id); + if (contentItem == null) { - var contentItem = await _contentManager.GetAsync(Id); - if (contentItem == null) - { - return NotFound(); - } - await _contentManager.RemoveAsync(contentItem); - - return RedirectToPage("/Foo/List"); + return NotFound(); } + await _contentManager.RemoveAsync(contentItem); - public async Task OnPostUpdate() - { - var contentItem = await _contentManager.GetAsync(Id); - - if (contentItem == null) - { - return NotFound(); - } + return RedirectToPage("/Foo/List"); + } - var updater = _updateModelAccessor.ModelUpdater; - _ = await _contentDisplay.UpdateEditorAsync(contentItem, updater, false); + public async Task OnPostUpdate() + { + var contentItem = await _contentManager.GetAsync(Id); - if (!ModelState.IsValid) - { - await _session.CancelAsync(); - return Page(); - } + if (contentItem == null) + { + return NotFound(); + } - contentItem.Content.TestContentPartA.Line = Text; - await _session.SaveAsync(contentItem); + var updater = _updateModelAccessor.ModelUpdater; + _ = await _contentDisplay.UpdateEditorAsync(contentItem, updater, false); - return RedirectToPage("/Foo/List"); + if (!ModelState.IsValid) + { + await _session.CancelAsync(); + return Page(); } + + contentItem.Content.TestContentPartA.Line = Text; + await _session.SaveAsync(contentItem); + + return RedirectToPage("/Foo/List"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/List.cshtml.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/List.cshtml.cs index f0ea70635d6..a57c5cd9d7a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/List.cshtml.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Pages/Foo/List.cshtml.cs @@ -10,60 +10,59 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.Demo.Pages +namespace OrchardCore.Demo.Pages; + +[Feature("OrchardCore.Demo.Foo")] +public class ListModel : PageModel { - [Feature("OrchardCore.Demo.Foo")] - public class ListModel : PageModel - { - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentDisplay; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly ISession _session; + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentDisplay; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly ISession _session; - public string Title { get; private set; } - public List Items { get; private set; } + public string Title { get; private set; } + public List Items { get; private set; } - [BindProperty] - public string Text { get; set; } + [BindProperty] + public string Text { get; set; } - public ListModel( - IContentManager contentManager, - IContentItemDisplayManager contentDisplay, - IUpdateModelAccessor updateModelAccessor, - ISession session) - { - _contentManager = contentManager; - _contentDisplay = contentDisplay; - _updateModelAccessor = updateModelAccessor; - _session = session; - } + public ListModel( + IContentManager contentManager, + IContentItemDisplayManager contentDisplay, + IUpdateModelAccessor updateModelAccessor, + ISession session) + { + _contentManager = contentManager; + _contentDisplay = contentDisplay; + _updateModelAccessor = updateModelAccessor; + _session = session; + } - public async Task OnGetAsync(string _) - { - var query = _session.Query() - .With(x => x.ContentType == "Foo" && x.Published); + public async Task OnGetAsync(string _) + { + var query = _session.Query() + .With(x => x.ContentType == "Foo" && x.Published); - var contentItems = await query.ListAsync(); - var updater = _updateModelAccessor.ModelUpdater; + var contentItems = await query.ListAsync(); + var updater = _updateModelAccessor.ModelUpdater; - Items = []; - Title = "Foo List"; + Items = []; + Title = "Foo List"; - foreach (var contentItem in contentItems) - { - Items.Add(await _contentDisplay.BuildDisplayAsync(contentItem, updater)); - } + foreach (var contentItem in contentItems) + { + Items.Add(await _contentDisplay.BuildDisplayAsync(contentItem, updater)); } + } - public async Task OnPostAsync() - { - var contentItem = await _contentManager.NewAsync("Foo"); + public async Task OnPostAsync() + { + var contentItem = await _contentManager.NewAsync("Foo"); - // Dynamic syntax - contentItem.Content.TestContentPartA.Line = Text; - await _contentManager.CreateAsync(contentItem); + // Dynamic syntax + contentItem.Content.TestContentPartA.Line = Text; + await _contentManager.CreateAsync(contentItem); - return RedirectToPage(); - } + return RedirectToPage(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestBackgroundTask.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestBackgroundTask.cs index 0f96872c468..8322c266703 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestBackgroundTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestBackgroundTask.cs @@ -3,19 +3,18 @@ using System.Threading.Tasks; using OrchardCore.BackgroundTasks; -namespace OrchardCore.Demo.Services +namespace OrchardCore.Demo.Services; + +public sealed class TestBackgroundTask : IBackgroundTask { - public sealed class TestBackgroundTask : IBackgroundTask - { #pragma warning disable IDE0052 // Remove unread private members - private int _count; + private int _count; #pragma warning restore IDE0052 // Remove unread private members - public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) - { - _count++; + public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + _count++; - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestDependency.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestDependency.cs index 0bb8408a71f..8267b56371f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestDependency.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Services/TestDependency.cs @@ -1,23 +1,22 @@ using OrchardCore.Environment.Shell; -namespace OrchardCore.Demo.Services +namespace OrchardCore.Demo.Services; + +public interface ITestDependency { - public interface ITestDependency + string SayHi(string line); +} + +public class ClassFoo : ITestDependency +{ + private readonly ShellSettings _shellSettings; + public ClassFoo(ShellSettings shellSettings) { - string SayHi(string line); + _shellSettings = shellSettings; } - public class ClassFoo : ITestDependency + public string SayHi(string line) { - private readonly ShellSettings _shellSettings; - public ClassFoo(ShellSettings shellSettings) - { - _shellSettings = shellSettings; - } - - public string SayHi(string line) - { - return string.Format("Hi from tenant {0} - {1}", _shellSettings.Name, line); - } + return string.Format("Hi from tenant {0} - {1}", _shellSettings.Name, line); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Services/UserProfileClaimsProvider.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Services/UserProfileClaimsProvider.cs index 738bf9a3dc3..294ac69ed25 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Services/UserProfileClaimsProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Services/UserProfileClaimsProvider.cs @@ -9,52 +9,51 @@ using OrchardCore.Users.Models; using OrchardCore.Users.Services; -namespace OrchardCore.Demo.Services +namespace OrchardCore.Demo.Services; + +internal sealed class UserProfileClaimsProvider : IUserClaimsProvider { - internal sealed class UserProfileClaimsProvider : IUserClaimsProvider + public Task GenerateAsync(IUser user, ClaimsIdentity claims) { - public Task GenerateAsync(IUser user, ClaimsIdentity claims) - { - ArgumentNullException.ThrowIfNull(user); - - ArgumentNullException.ThrowIfNull(claims); - - var u = user as User; - var profile = u.As(); + ArgumentNullException.ThrowIfNull(user); - claims.AddClaim(new Claim("preferred_username", user.UserName)); + ArgumentNullException.ThrowIfNull(claims); - var name = ""; - if (!string.IsNullOrEmpty(profile.FirstName)) - { - claims.AddClaim(new Claim("given_name", profile.FirstName)); - name += profile.FirstName; - } + var u = user as User; + var profile = u.As(); - if (!string.IsNullOrEmpty(profile.LastName)) - { - claims.AddClaim(new Claim("family_name", profile.LastName)); - name += $" {profile.LastName}"; - } + claims.AddClaim(new Claim("preferred_username", user.UserName)); - if (!string.IsNullOrEmpty(name)) - { - claims.AddClaim(new Claim("name", name)); - } + var name = ""; + if (!string.IsNullOrEmpty(profile.FirstName)) + { + claims.AddClaim(new Claim("given_name", profile.FirstName)); + name += profile.FirstName; + } - if (profile.UpdatedAt != default) - { - claims.AddClaim(new Claim("updated_at", ConvertToUnixTimestamp(profile.UpdatedAt).ToString(CultureInfo.InvariantCulture))); - } + if (!string.IsNullOrEmpty(profile.LastName)) + { + claims.AddClaim(new Claim("family_name", profile.LastName)); + name += $" {profile.LastName}"; + } - return Task.FromResult(claims); + if (!string.IsNullOrEmpty(name)) + { + claims.AddClaim(new Claim("name", name)); } - public static double ConvertToUnixTimestamp(DateTime date) + if (profile.UpdatedAt != default) { - var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - var diff = date.ToUniversalTime() - origin; - return Math.Floor(diff.TotalSeconds); + claims.AddClaim(new Claim("updated_at", ConvertToUnixTimestamp(profile.UpdatedAt).ToString(CultureInfo.InvariantCulture))); } + + return Task.FromResult(claims); + } + + public static double ConvertToUnixTimestamp(DateTime date) + { + var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var diff = date.ToUniversalTime() - origin; + return Math.Floor(diff.TotalSeconds); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/BazTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/BazTagHelper.cs index 7ee332ea1be..52716f8603e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/BazTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/BazTagHelper.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.Razor.TagHelpers; -namespace OrchardCore.Demo.TagHelpers.Intellisense +namespace OrchardCore.Demo.TagHelpers.Intellisense; + +public class BazTagHelper : TagHelper { - public class BazTagHelper : TagHelper - { - /// - /// The text to render. - /// - public string Text { get; set; } + /// + /// The text to render. + /// + public string Text { get; set; } - /// - /// the number of times to repeat the text. - /// - public int Count { get; set; } - } + /// + /// the number of times to repeat the text. + /// + public int Count { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/DemoShapeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/DemoShapeProvider.cs index 9604d50603a..f47c939e02c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/DemoShapeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Shapes/DemoShapeProvider.cs @@ -2,32 +2,31 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Html; -namespace OrchardCore.DisplayManagement.Descriptors +namespace OrchardCore.DisplayManagement.Descriptors; + +public class DemoShapeProvider : ShapeTableProvider, IShapeAttributeProvider { - public class DemoShapeProvider : ShapeTableProvider, IShapeAttributeProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("Foo") - .OnDisplaying(displaying => - displaying.ChildContent = new HtmlString("

Hi

") - ); + builder.Describe("Foo") + .OnDisplaying(displaying => + displaying.ChildContent = new HtmlString("

Hi

") + ); - return ValueTask.CompletedTask; - } + return ValueTask.CompletedTask; + } - [Shape] + [Shape] #pragma warning disable CA1822 // Mark members as static - public IHtmlContent Baz(string text, int count) + public IHtmlContent Baz(string text, int count) #pragma warning restore CA1822 // Mark members as static + { + var sb = new StringBuilder(); + for (var i = 0; i < count; i++) { - var sb = new StringBuilder(); - for (var i = 0; i < count; i++) - { - sb.Append(text); - } - - return new HtmlString(sb.ToString()); + sb.Append(text); } + + return new HtmlString(sb.ToString()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Demo/Startup.cs index 3c3d46efedf..8892c08f4e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/Startup.cs @@ -28,101 +28,100 @@ using OrchardCore.Users.Models; using OrchardCore.Users.Services; -namespace OrchardCore.Demo +namespace OrchardCore.Demo; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly AdminOptions _adminOptions; + + public Startup(IOptions adminOptions) { - private readonly AdminOptions _adminOptions; + _adminOptions = adminOptions.Value; + } - public Startup(IOptions adminOptions) - { - _adminOptions = adminOptions.Value; - } + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "Demo.Home.Index", + areaName: "OrchardCore.Demo", + pattern: "Home/Index", + defaults: new { controller = "Home", action = "Index" } + ); + + routes.MapAreaControllerRoute( + name: "Demo.Home.Display", + areaName: "OrchardCore.Demo", + pattern: "Home/Display/{contentItemId}", + defaults: new { controller = "Home", action = "Display" } + ); + + routes.MapAreaControllerRoute( + name: "Demo.Home.Error", + areaName: "OrchardCore.Demo", + pattern: "Home/IndexError", + defaults: new { controller = "Home", action = "IndexError" } + ); + + var demoAdminControllerName = typeof(AdminController).ControllerName(); + + // While you can define admin routes like this, we suggest adding the [Admin("path after the admin prefix")] + // attribute to the action's method instead. That way the route is visible right next to the action which + // makes the code easier to understand. You can find an example in this module at ContentController.Edit. + routes.MapAreaControllerRoute( + name: "Demo.Admin", + areaName: "OrchardCore.Demo", + pattern: _adminOptions.AdminUrlPrefix + "/Demo/Admin", + defaults: new { controller = demoAdminControllerName, action = nameof(AdminController.Index) } + ); + + var demoContentControllerName = typeof(ContentController).ControllerName(); + + builder.UseMiddleware(); + builder.UseMiddleware(); + } - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddScoped(); + services.AddShapeAttributes(); + services.AddScoped(); + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); + services.AddContentPart(); + services.AddScoped(); + + services.AddScoped, UserProfileDisplayDriver>(); + + services.Configure(options => { - routes.MapAreaControllerRoute( - name: "Demo.Home.Index", - areaName: "OrchardCore.Demo", - pattern: "Home/Index", - defaults: new { controller = "Home", action = "Index" } - ); - - routes.MapAreaControllerRoute( - name: "Demo.Home.Display", - areaName: "OrchardCore.Demo", - pattern: "Home/Display/{contentItemId}", - defaults: new { controller = "Home", action = "Display" } - ); - - routes.MapAreaControllerRoute( - name: "Demo.Home.Error", - areaName: "OrchardCore.Demo", - pattern: "Home/IndexError", - defaults: new { controller = "Home", action = "IndexError" } - ); - - var demoAdminControllerName = typeof(AdminController).ControllerName(); - - // While you can define admin routes like this, we suggest adding the [Admin("path after the admin prefix")] - // attribute to the action's method instead. That way the route is visible right next to the action which - // makes the code easier to understand. You can find an example in this module at ContentController.Edit. - routes.MapAreaControllerRoute( - name: "Demo.Admin", - areaName: "OrchardCore.Demo", - pattern: _adminOptions.AdminUrlPrefix + "/Demo/Admin", - defaults: new { controller = demoAdminControllerName, action = nameof(AdminController.Index) } - ); - - var demoContentControllerName = typeof(ContentController).ControllerName(); - - builder.UseMiddleware(); - builder.UseMiddleware(); - } - - public override void ConfigureServices(IServiceCollection services) + // Add a custom page folder route (only applied to non admin pages) + options.Conventions.AddAreaFolderRoute("OrchardCore.Demo", "/", "Demo"); + + // Add a custom admin page folder route (only applied to admin pages) using the current admin prefix + options.Conventions.AddAdminAreaFolderRoute("OrchardCore.Demo", "/Admin", _adminOptions.AdminUrlPrefix + "/Demo"); + + // Add a custom admin page folder route without using the current admin prefix + options.Conventions.AddAdminAreaFolderRoute("OrchardCore.Demo", "/Foo/Admin", "Manage/Foo"); + + // Add a custom admin page route using the current admin prefix + options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/OutsideAdmin", _adminOptions.AdminUrlPrefix + "/Outside"); + + // Add a custom page route + options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/Hello", "Hello"); + + // This declaration would define an home page + // options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/Hello", ""); + }); + + services.AddTagHelpers(typeof(BazTagHelper).Assembly); + + services.Configure(o => { - services.AddScoped(); - services.AddScoped(); - services.AddSingleton(); - services.AddScoped(); - services.AddShapeAttributes(); - services.AddScoped(); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); - services.AddContentPart(); - services.AddScoped(); - - services.AddScoped, UserProfileDisplayDriver>(); - - services.Configure(options => - { - // Add a custom page folder route (only applied to non admin pages) - options.Conventions.AddAreaFolderRoute("OrchardCore.Demo", "/", "Demo"); - - // Add a custom admin page folder route (only applied to admin pages) using the current admin prefix - options.Conventions.AddAdminAreaFolderRoute("OrchardCore.Demo", "/Admin", _adminOptions.AdminUrlPrefix + "/Demo"); - - // Add a custom admin page folder route without using the current admin prefix - options.Conventions.AddAdminAreaFolderRoute("OrchardCore.Demo", "/Foo/Admin", "Manage/Foo"); - - // Add a custom admin page route using the current admin prefix - options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/OutsideAdmin", _adminOptions.AdminUrlPrefix + "/Outside"); - - // Add a custom page route - options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/Hello", "Hello"); - - // This declaration would define an home page - // options.Conventions.AddAreaPageRoute("OrchardCore.Demo", "/Hello", ""); - }); - - services.AddTagHelpers(typeof(BazTagHelper).Assembly); - - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }); - } + o.MemberAccessStrategy.Register(); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/TagHelpers/BazTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Demo/TagHelpers/BazTagHelper.cs index 2b83139e5fd..4b1805b1920 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/TagHelpers/BazTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/TagHelpers/BazTagHelper.cs @@ -2,15 +2,14 @@ using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.TagHelpers; -namespace OrchardCore.Demo.TagHelpers +namespace OrchardCore.Demo.TagHelpers; + +[HtmlTargetElement("baz")] +public class BazTagHelper : BaseShapeTagHelper { - [HtmlTargetElement("baz")] - public class BazTagHelper : BaseShapeTagHelper + public BazTagHelper(IShapeFactory shapeFactory, IDisplayHelper displayHelper) + : base(shapeFactory, displayHelper) { - public BazTagHelper(IShapeFactory shapeFactory, IDisplayHelper displayHelper) - : base(shapeFactory, displayHelper) - { - Type = "Baz"; - } + Type = "Baz"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/EditUserProfileViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/EditUserProfileViewModel.cs index da6f15127ba..1eea762b18d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/EditUserProfileViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/EditUserProfileViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Demo.ViewModels +namespace OrchardCore.Demo.ViewModels; + +public class EditUserProfileViewModel { - public class EditUserProfileViewModel - { - public int Age { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - } + public int Age { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/TodoViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/TodoViewModel.cs index 32d71c7eb93..b2b64f1b608 100644 --- a/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/TodoViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Demo/ViewModels/TodoViewModel.cs @@ -2,47 +2,46 @@ using System.ComponentModel.DataAnnotations; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Demo.ViewModels +namespace OrchardCore.Demo.ViewModels; + +public class TodoViewModel : ShapeViewModel { - public class TodoViewModel : ShapeViewModel + public TodoViewModel() + : base("Todo") { - public TodoViewModel() - : base("Todo") - { - } + } - public string TodoId { get; set; } + public string TodoId { get; set; } - [Required] - public string Text { get; set; } + [Required] + public string Text { get; set; } - [Required] - public DateTime DueDate { get; set; } + [Required] + public DateTime DueDate { get; set; } - public bool IsCompleted { get; set; } + public bool IsCompleted { get; set; } - public string DisplayMode + public string DisplayMode + { + get { - get - { - return Metadata.DisplayType; - } - set + return Metadata.DisplayType; + } + set + { + var alternate = $"Todo_{value}"; + if (Metadata.Alternates.Contains(alternate)) { - var alternate = $"Todo_{value}"; - if (Metadata.Alternates.Contains(alternate)) + if (Metadata.Alternates.Last == alternate) { - if (Metadata.Alternates.Last == alternate) - { - return; - } - - Metadata.Alternates.Remove(alternate); + return; } - Metadata.Alternates.Add(alternate); - Metadata.DisplayType = value; + Metadata.Alternates.Remove(alternate); } + + Metadata.Alternates.Add(alternate); + Metadata.DisplayType = value; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/AdminMenu.cs index cb638810145..57990648c60 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/AdminMenu.cs @@ -2,41 +2,40 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Deployment.Remote +namespace OrchardCore.Deployment.Remote; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Import/Export"], import => import - .Add(S["Remote Instances"], S["Remote Instances"].PrefixPosition(), remote => remote - .Action("Index", "RemoteInstance", "OrchardCore.Deployment.Remote") - .Permission(Permissions.ManageRemoteInstances) - .LocalNav() - ) - .Add(S["Remote Clients"], S["Remote Clients"].PrefixPosition(), remote => remote - .Action("Index", "RemoteClient", "OrchardCore.Deployment.Remote") - .Permission(Permissions.ManageRemoteClients) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Import/Export"], import => import + .Add(S["Remote Instances"], S["Remote Instances"].PrefixPosition(), remote => remote + .Action("Index", "RemoteInstance", "OrchardCore.Deployment.Remote") + .Permission(Permissions.ManageRemoteInstances) + .LocalNav() + ) + .Add(S["Remote Clients"], S["Remote Clients"].PrefixPosition(), remote => remote + .Action("Index", "RemoteClient", "OrchardCore.Deployment.Remote") + .Permission(Permissions.ManageRemoteClients) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ExportRemoteInstanceController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ExportRemoteInstanceController.cs index 750c2912e1c..bd646c27787 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ExportRemoteInstanceController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ExportRemoteInstanceController.cs @@ -15,116 +15,115 @@ using OrchardCore.Recipes.Models; using YesSql; -namespace OrchardCore.Deployment.Remote.Controllers +namespace OrchardCore.Deployment.Remote.Controllers; + +[Admin("Deployment/ExportRemoteInstance/{action}/{id?}", "DeploymentExportRemoteInstance{action}")] +public class ExportRemoteInstanceController : Controller { - [Admin("Deployment/ExportRemoteInstance/{action}/{id?}", "DeploymentExportRemoteInstance{action}")] - public class ExportRemoteInstanceController : Controller + private readonly IDeploymentManager _deploymentManager; + private readonly IAuthorizationService _authorizationService; + private readonly ISession _session; + private readonly RemoteInstanceService _service; + private readonly INotifier _notifier; + private readonly IHttpClientFactory _httpClientFactory; + protected readonly IHtmlLocalizer H; + + public ExportRemoteInstanceController( + IAuthorizationService authorizationService, + ISession session, + RemoteInstanceService service, + IDeploymentManager deploymentManager, + INotifier notifier, + IHttpClientFactory httpClientFactory, + IHtmlLocalizer localizer) + { + _authorizationService = authorizationService; + _deploymentManager = deploymentManager; + _session = session; + _service = service; + _notifier = notifier; + _httpClientFactory = httpClientFactory; + H = localizer; + } + + [HttpPost] + public async Task Execute(long id, string remoteInstanceId, string returnUrl) { - private readonly IDeploymentManager _deploymentManager; - private readonly IAuthorizationService _authorizationService; - private readonly ISession _session; - private readonly RemoteInstanceService _service; - private readonly INotifier _notifier; - private readonly IHttpClientFactory _httpClientFactory; - protected readonly IHtmlLocalizer H; - - public ExportRemoteInstanceController( - IAuthorizationService authorizationService, - ISession session, - RemoteInstanceService service, - IDeploymentManager deploymentManager, - INotifier notifier, - IHttpClientFactory httpClientFactory, - IHtmlLocalizer localizer) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.Export)) { - _authorizationService = authorizationService; - _deploymentManager = deploymentManager; - _session = session; - _service = service; - _notifier = notifier; - _httpClientFactory = httpClientFactory; - H = localizer; + return Forbid(); } - [HttpPost] - public async Task Execute(long id, string remoteInstanceId, string returnUrl) + var deploymentPlan = await _session.GetAsync(id); + + if (deploymentPlan == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.Export)) - { - return Forbid(); - } + return NotFound(); + } - var deploymentPlan = await _session.GetAsync(id); + var remoteInstance = await _service.GetRemoteInstanceAsync(remoteInstanceId); - if (deploymentPlan == null) - { - return NotFound(); - } + if (remoteInstance == null) + { + return NotFound(); + } - var remoteInstance = await _service.GetRemoteInstanceAsync(remoteInstanceId); + string archiveFileName; + var filename = deploymentPlan.Name.ToSafeName() + ".zip"; - if (remoteInstance == null) - { - return NotFound(); - } + using (var fileBuilder = new TemporaryFileBuilder()) + { + archiveFileName = PathExtensions.Combine(Path.GetTempPath(), filename); - string archiveFileName; - var filename = deploymentPlan.Name.ToSafeName() + ".zip"; + var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, new RecipeDescriptor()); + await _deploymentManager.ExecuteDeploymentPlanAsync(deploymentPlan, deploymentPlanResult); - using (var fileBuilder = new TemporaryFileBuilder()) + if (System.IO.File.Exists(archiveFileName)) { - archiveFileName = PathExtensions.Combine(Path.GetTempPath(), filename); + System.IO.File.Delete(archiveFileName); + } - var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, new RecipeDescriptor()); - await _deploymentManager.ExecuteDeploymentPlanAsync(deploymentPlan, deploymentPlanResult); + ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName); + } - if (System.IO.File.Exists(archiveFileName)) - { - System.IO.File.Delete(archiveFileName); - } + HttpResponseMessage response; - ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName); - } + try + { + using (var requestContent = new MultipartFormDataContent()) + { + requestContent.Add(new StreamContent( + new FileStream(archiveFileName, + FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1, FileOptions.Asynchronous | FileOptions.SequentialScan) + ), + nameof(ImportViewModel.Content), Path.GetFileName(archiveFileName)); + requestContent.Add(new StringContent(remoteInstance.ClientName), nameof(ImportViewModel.ClientName)); + requestContent.Add(new StringContent(remoteInstance.ApiKey), nameof(ImportViewModel.ApiKey)); - HttpResponseMessage response; + var httpClient = _httpClientFactory.CreateClient(); - try - { - using (var requestContent = new MultipartFormDataContent()) - { - requestContent.Add(new StreamContent( - new FileStream(archiveFileName, - FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1, FileOptions.Asynchronous | FileOptions.SequentialScan) - ), - nameof(ImportViewModel.Content), Path.GetFileName(archiveFileName)); - requestContent.Add(new StringContent(remoteInstance.ClientName), nameof(ImportViewModel.ClientName)); - requestContent.Add(new StringContent(remoteInstance.ApiKey), nameof(ImportViewModel.ApiKey)); - - var httpClient = _httpClientFactory.CreateClient(); - - response = await httpClient.PostAsync(remoteInstance.Url, requestContent); - } - - if (response.StatusCode == System.Net.HttpStatusCode.OK) - { - await _notifier.SuccessAsync(H["Deployment executed successfully."]); - } - else - { - await _notifier.ErrorAsync(H["An error occurred while sending the deployment to the remote instance: \"{0} ({1})\"", response.ReasonPhrase, (int)response.StatusCode]); - } + response = await httpClient.PostAsync(remoteInstance.Url, requestContent); } - finally + + if (response.StatusCode == System.Net.HttpStatusCode.OK) { - System.IO.File.Delete(archiveFileName); + await _notifier.SuccessAsync(H["Deployment executed successfully."]); } - - if (!string.IsNullOrEmpty(returnUrl)) + else { - return this.LocalRedirect(returnUrl, true); + await _notifier.ErrorAsync(H["An error occurred while sending the deployment to the remote instance: \"{0} ({1})\"", response.ReasonPhrase, (int)response.StatusCode]); } + } + finally + { + System.IO.File.Delete(archiveFileName); + } - return RedirectToAction("Display", "DeploymentPlan", new { area = "OrchardCore.Deployment", id }); + if (!string.IsNullOrEmpty(returnUrl)) + { + return this.LocalRedirect(returnUrl, true); } + + return RedirectToAction("Display", "DeploymentPlan", new { area = "OrchardCore.Deployment", id }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ImportRemoteInstanceController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ImportRemoteInstanceController.cs index b0cacb21222..0a09436f9eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ImportRemoteInstanceController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/ImportRemoteInstanceController.cs @@ -17,104 +17,103 @@ using OrchardCore.DisplayManagement.Notify; using OrchardCore.Recipes.Models; -namespace OrchardCore.Deployment.Remote.Controllers +namespace OrchardCore.Deployment.Remote.Controllers; + +public class ImportRemoteInstanceController : Controller { - public class ImportRemoteInstanceController : Controller + private readonly RemoteClientService _remoteClientService; + private readonly IDeploymentManager _deploymentManager; + private readonly INotifier _notifier; + private readonly ILogger _logger; + private readonly IDataProtector _dataProtector; + + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + + public ImportRemoteInstanceController( + IDataProtectionProvider dataProtectionProvider, + RemoteClientService remoteClientService, + IDeploymentManager deploymentManager, + INotifier notifier, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + ILogger logger) + { + _deploymentManager = deploymentManager; + _notifier = notifier; + _logger = logger; + _remoteClientService = remoteClientService; + H = htmlLocalizer; + S = stringLocalizer; + _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); + } + + /// + /// We ignore the AFT as the service is called from external applications (they can't have valid ones) and + /// we use a private API key to secure its calls. + /// + [HttpPost] + [IgnoreAntiforgeryToken] + public async Task Import(ImportViewModel model) { - private readonly RemoteClientService _remoteClientService; - private readonly IDeploymentManager _deploymentManager; - private readonly INotifier _notifier; - private readonly ILogger _logger; - private readonly IDataProtector _dataProtector; - - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - - public ImportRemoteInstanceController( - IDataProtectionProvider dataProtectionProvider, - RemoteClientService remoteClientService, - IDeploymentManager deploymentManager, - INotifier notifier, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer, - ILogger logger) + var remoteClientList = await _remoteClientService.GetRemoteClientListAsync(); + + var remoteClient = remoteClientList.RemoteClients.FirstOrDefault(x => x.ClientName == model.ClientName); + + if (remoteClient == null) { - _deploymentManager = deploymentManager; - _notifier = notifier; - _logger = logger; - _remoteClientService = remoteClientService; - H = htmlLocalizer; - S = stringLocalizer; - _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); + return StatusCode((int)HttpStatusCode.BadRequest, "The remote client was not provided"); } - /// - /// We ignore the AFT as the service is called from external applications (they can't have valid ones) and - /// we use a private API key to secure its calls. - /// - [HttpPost] - [IgnoreAntiforgeryToken] - public async Task Import(ImportViewModel model) - { - var remoteClientList = await _remoteClientService.GetRemoteClientListAsync(); + var apiKey = Encoding.UTF8.GetString(_dataProtector.Unprotect(remoteClient.ProtectedApiKey)); - var remoteClient = remoteClientList.RemoteClients.FirstOrDefault(x => x.ClientName == model.ClientName); + if (model.ApiKey != apiKey || model.ClientName != remoteClient.ClientName) + { + return StatusCode((int)HttpStatusCode.BadRequest, "The Api Key was not recognized"); + } - if (remoteClient == null) - { - return StatusCode((int)HttpStatusCode.BadRequest, "The remote client was not provided"); - } + // Create a temporary filename to save the archive + var tempArchiveName = Path.GetTempFileName() + ".zip"; - var apiKey = Encoding.UTF8.GetString(_dataProtector.Unprotect(remoteClient.ProtectedApiKey)); + // Create a temporary folder to extract the archive to + var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - if (model.ApiKey != apiKey || model.ClientName != remoteClient.ClientName) + try + { + using (var fs = System.IO.File.Create(tempArchiveName)) { - return StatusCode((int)HttpStatusCode.BadRequest, "The Api Key was not recognized"); + await model.Content.CopyToAsync(fs); } - // Create a temporary filename to save the archive - var tempArchiveName = Path.GetTempFileName() + ".zip"; - - // Create a temporary folder to extract the archive to - var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + ZipFile.ExtractToDirectory(tempArchiveName, tempArchiveFolder); - try - { - using (var fs = System.IO.File.Create(tempArchiveName)) - { - await model.Content.CopyToAsync(fs); - } + await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); + } + catch (RecipeExecutionException e) + { + _logger.LogError(e, "Unable to import a recipe from deployment plan."); - ZipFile.ExtractToDirectory(tempArchiveName, tempArchiveFolder); + await _notifier.ErrorAsync(H["The deployment plan failed with the following errors: {0}", string.Join(' ', e.StepResult.Errors)]); + } + catch (Exception e) + { + _logger.LogError(e, "Unexpected error occurred while executing a deployment plan."); - await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); - } - catch (RecipeExecutionException e) + await _notifier.ErrorAsync(H["Unexpected error occurred while executing a deployment plan."]); + } + finally + { + if (System.IO.File.Exists(tempArchiveName)) { - _logger.LogError(e, "Unable to import a recipe from deployment plan."); - - await _notifier.ErrorAsync(H["The deployment plan failed with the following errors: {0}", string.Join(' ', e.StepResult.Errors)]); + System.IO.File.Delete(tempArchiveName); } - catch (Exception e) - { - _logger.LogError(e, "Unexpected error occurred while executing a deployment plan."); - await _notifier.ErrorAsync(H["Unexpected error occurred while executing a deployment plan."]); - } - finally + if (Directory.Exists(tempArchiveFolder)) { - if (System.IO.File.Exists(tempArchiveName)) - { - System.IO.File.Delete(tempArchiveName); - } - - if (Directory.Exists(tempArchiveFolder)) - { - Directory.Delete(tempArchiveFolder, true); - } + Directory.Delete(tempArchiveFolder, true); } - - return Ok(); } + + return Ok(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteClientController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteClientController.cs index 98893f63274..dc153e20c30 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteClientController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteClientController.cs @@ -19,258 +19,257 @@ using OrchardCore.Navigation; using OrchardCore.Routing; -namespace OrchardCore.Deployment.Remote.Controllers +namespace OrchardCore.Deployment.Remote.Controllers; + +[Admin("Deployment/RemoteClient/{action}/{id?}", "DeploymentRemoteClient{action}")] +public class RemoteClientController : Controller { - [Admin("Deployment/RemoteClient/{action}/{id?}", "DeploymentRemoteClient{action}")] - public class RemoteClientController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IDataProtector _dataProtector; + private readonly IAuthorizationService _authorizationService; + private readonly PagerOptions _pagerOptions; + private readonly IShapeFactory _shapeFactory; + private readonly RemoteClientService _remoteClientService; + private readonly INotifier _notifier; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public RemoteClientController( + IDataProtectionProvider dataProtectionProvider, + RemoteClientService remoteClientService, + IAuthorizationService authorizationService, + IOptions pagerOptions, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier + ) + { + _authorizationService = authorizationService; + _pagerOptions = pagerOptions.Value; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + _notifier = notifier; + _remoteClientService = remoteClientService; + _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); + } + + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly IDataProtector _dataProtector; - private readonly IAuthorizationService _authorizationService; - private readonly PagerOptions _pagerOptions; - private readonly IShapeFactory _shapeFactory; - private readonly RemoteClientService _remoteClientService; - private readonly INotifier _notifier; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public RemoteClientController( - IDataProtectionProvider dataProtectionProvider, - RemoteClientService remoteClientService, - IAuthorizationService authorizationService, - IOptions pagerOptions, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier - ) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) { - _authorizationService = authorizationService; - _pagerOptions = pagerOptions.Value; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - _notifier = notifier; - _remoteClientService = remoteClientService; - _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); + return Forbid(); } - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var remoteClients = (await _remoteClientService.GetRemoteClientListAsync()).RemoteClients; - var remoteClients = (await _remoteClientService.GetRemoteClientListAsync()).RemoteClients; + if (!string.IsNullOrWhiteSpace(options.Search)) + { + remoteClients = remoteClients.Where(x => x.ClientName.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - if (!string.IsNullOrWhiteSpace(options.Search)) - { - remoteClients = remoteClients.Where(x => x.ClientName.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + var count = remoteClients.Count; - var count = remoteClients.Count; + var startIndex = pager.GetStartIndex(); + var pageSize = pager.PageSize; - var startIndex = pager.GetStartIndex(); - var pageSize = pager.PageSize; + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var model = new RemoteClientIndexViewModel + { + RemoteClients = remoteClients, + Pager = pagerShape, + Options = options + }; - var model = new RemoteClientIndexViewModel - { - RemoteClients = remoteClients, - Pager = pagerShape, - Options = options - }; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; + + return View(model); + } - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(RemoteClientIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - return View(model); + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(RemoteClientIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + var model = new EditRemoteClientViewModel(); - public async Task Create() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } + return View(model); + } - var model = new EditRemoteClientViewModel(); + [HttpPost] + public async Task Create(EditRemoteClientViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) + { + return Forbid(); + } - return View(model); + if (ModelState.IsValid) + { + ValidateViewModel(model); } - [HttpPost] - public async Task Create(EditRemoteClientViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } + await _remoteClientService.CreateRemoteClientAsync(model.ClientName, model.ApiKey); - if (ModelState.IsValid) - { - ValidateViewModel(model); - } + await _notifier.SuccessAsync(H["Remote client created successfully."]); + return RedirectToAction(nameof(Index)); + } - if (ModelState.IsValid) - { - await _remoteClientService.CreateRemoteClientAsync(model.ClientName, model.ApiKey); + // If we got this far, something failed, redisplay form + return View(model); + } - await _notifier.SuccessAsync(H["Remote client created successfully."]); - return RedirectToAction(nameof(Index)); - } + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) + { + return Forbid(); + } + + var remoteClient = await _remoteClientService.GetRemoteClientAsync(id); - // If we got this far, something failed, redisplay form - return View(model); + if (remoteClient == null) + { + return NotFound(); } - public async Task Edit(string id) + var model = new EditRemoteClientViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } + Id = remoteClient.Id, + ClientName = remoteClient.ClientName, + ApiKey = Encoding.UTF8.GetString(_dataProtector.Unprotect(remoteClient.ProtectedApiKey)), + }; - var remoteClient = await _remoteClientService.GetRemoteClientAsync(id); + return View(model); + } - if (remoteClient == null) - { - return NotFound(); - } + [HttpPost] + public async Task Edit(EditRemoteClientViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) + { + return Forbid(); + } - var model = new EditRemoteClientViewModel - { - Id = remoteClient.Id, - ClientName = remoteClient.ClientName, - ApiKey = Encoding.UTF8.GetString(_dataProtector.Unprotect(remoteClient.ProtectedApiKey)), - }; + var remoteClient = await _remoteClientService.GetRemoteClientAsync(model.Id); - return View(model); + if (remoteClient == null) + { + return NotFound(); } - [HttpPost] - public async Task Edit(EditRemoteClientViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } - - var remoteClient = await _remoteClientService.GetRemoteClientAsync(model.Id); - - if (remoteClient == null) - { - return NotFound(); - } + ValidateViewModel(model); + } - if (ModelState.IsValid) - { - ValidateViewModel(model); - } + if (ModelState.IsValid) + { + await _remoteClientService.TryUpdateRemoteClient(model.Id, model.ClientName, model.ApiKey); - if (ModelState.IsValid) - { - await _remoteClientService.TryUpdateRemoteClient(model.Id, model.ClientName, model.ApiKey); + await _notifier.SuccessAsync(H["Remote client updated successfully."]); - await _notifier.SuccessAsync(H["Remote client updated successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); - } + // If we got this far, something failed, redisplay form + return View(model); + } - // If we got this far, something failed, redisplay form - return View(model); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string id) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteClients)) - { - return Forbid(); - } + var remoteClient = await _remoteClientService.GetRemoteClientAsync(id); - var remoteClient = await _remoteClientService.GetRemoteClientAsync(id); + if (remoteClient == null) + { + return NotFound(); + } - if (remoteClient == null) - { - return NotFound(); - } + await _remoteClientService.DeleteRemoteClientAsync(id); - await _remoteClientService.DeleteRemoteClientAsync(id); + await _notifier.SuccessAsync(H["Remote client deleted successfully."]); - await _notifier.SuccessAsync(H["Remote client deleted successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ViewModels.ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); } - [HttpPost, ActionName("Index")] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ViewModels.ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + var remoteClients = (await _remoteClientService.GetRemoteClientListAsync()).RemoteClients; + var checkedContentItems = remoteClients.Where(x => itemIds.Contains(x.Id)).ToList(); - if (itemIds?.Count() > 0) + switch (options.BulkAction) { - var remoteClients = (await _remoteClientService.GetRemoteClientListAsync()).RemoteClients; - var checkedContentItems = remoteClients.Where(x => itemIds.Contains(x.Id)).ToList(); - - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _remoteClientService.DeleteRemoteClientAsync(item.Id); - } - await _notifier.SuccessAsync(H["Remote clients successfully removed."]); - break; - default: - return BadRequest(); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _remoteClientService.DeleteRemoteClientAsync(item.Id); + } + await _notifier.SuccessAsync(H["Remote clients successfully removed."]); + break; + default: + return BadRequest(); } - - return RedirectToAction("Index"); } - private void ValidateViewModel(EditRemoteClientViewModel model) + return RedirectToAction("Index"); + } + + private void ValidateViewModel(EditRemoteClientViewModel model) + { + if (string.IsNullOrWhiteSpace(model.ClientName)) { - if (string.IsNullOrWhiteSpace(model.ClientName)) - { - ModelState.AddModelError(nameof(EditRemoteClientViewModel.ClientName), S["The client name is mandatory."]); - } + ModelState.AddModelError(nameof(EditRemoteClientViewModel.ClientName), S["The client name is mandatory."]); + } - if (string.IsNullOrWhiteSpace(model.ApiKey)) - { - ModelState.AddModelError(nameof(EditRemoteClientViewModel.ApiKey), S["The api key is mandatory."]); - } + if (string.IsNullOrWhiteSpace(model.ApiKey)) + { + ModelState.AddModelError(nameof(EditRemoteClientViewModel.ApiKey), S["The api key is mandatory."]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteInstanceController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteInstanceController.cs index 4009ae9dfdf..c2e39e1dcb4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteInstanceController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Controllers/RemoteInstanceController.cs @@ -17,271 +17,270 @@ using OrchardCore.Navigation; using OrchardCore.Routing; -namespace OrchardCore.Deployment.Remote.Controllers +namespace OrchardCore.Deployment.Remote.Controllers; + +[Admin("Deployment/RemoteInstance/{action}/{id?}", "DeploymentRemoteInstancesCreate{action}")] +public class RemoteInstanceController : Controller { - [Admin("Deployment/RemoteInstance/{action}/{id?}", "DeploymentRemoteInstancesCreate{action}")] - public class RemoteInstanceController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly PagerOptions _pagerOptions; + private readonly IShapeFactory _shapeFactory; + private readonly INotifier _notifier; + private readonly RemoteInstanceService _service; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public RemoteInstanceController( + RemoteInstanceService service, + IAuthorizationService authorizationService, + IOptions pagerOptions, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier + ) + { + _authorizationService = authorizationService; + _pagerOptions = pagerOptions.Value; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + _notifier = notifier; + _service = service; + } + + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly PagerOptions _pagerOptions; - private readonly IShapeFactory _shapeFactory; - private readonly INotifier _notifier; - private readonly RemoteInstanceService _service; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public RemoteInstanceController( - RemoteInstanceService service, - IAuthorizationService authorizationService, - IOptions pagerOptions, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier - ) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) { - _authorizationService = authorizationService; - _pagerOptions = pagerOptions.Value; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - _notifier = notifier; - _service = service; + return Forbid(); } - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var remoteInstances = (await _service.GetRemoteInstanceListAsync()).RemoteInstances; - var remoteInstances = (await _service.GetRemoteInstanceListAsync()).RemoteInstances; + if (!string.IsNullOrWhiteSpace(options.Search)) + { + remoteInstances = remoteInstances.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - if (!string.IsNullOrWhiteSpace(options.Search)) - { - remoteInstances = remoteInstances.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + var startIndex = pager.GetStartIndex(); + var pageSize = pager.PageSize; - var startIndex = pager.GetStartIndex(); - var pageSize = pager.PageSize; + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + var pagerShape = await _shapeFactory.PagerAsync(pager, remoteInstances.Count, routeData); - var pagerShape = await _shapeFactory.PagerAsync(pager, remoteInstances.Count, routeData); + var model = new RemoteInstanceIndexViewModel + { + RemoteInstances = remoteInstances, + Pager = pagerShape, + Options = options + }; - var model = new RemoteInstanceIndexViewModel - { - RemoteInstances = remoteInstances, - Pager = pagerShape, - Options = options - }; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + return View(model); + } - return View(model); + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(RemoteInstanceIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); + + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(RemoteInstanceIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + var model = new EditRemoteInstanceViewModel(); - public async Task Create() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + return View(model); + } - var model = new EditRemoteInstanceViewModel(); + [HttpPost] + public async Task Create(EditRemoteInstanceViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); + } - return View(model); + if (ModelState.IsValid) + { + ValidateViewModel(model); } - [HttpPost] - public async Task Create(EditRemoteInstanceViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + await _service.CreateRemoteInstanceAsync(model.Name, model.Url, model.ClientName, model.ApiKey); - if (ModelState.IsValid) - { - ValidateViewModel(model); - } + await _notifier.SuccessAsync(H["Remote instance created successfully."]); + return RedirectToAction(nameof(Index)); + } - if (ModelState.IsValid) - { - await _service.CreateRemoteInstanceAsync(model.Name, model.Url, model.ClientName, model.ApiKey); + // If we got this far, something failed, redisplay form + return View(model); + } - await _notifier.SuccessAsync(H["Remote instance created successfully."]); - return RedirectToAction(nameof(Index)); - } + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); + } + + var remoteInstance = await _service.GetRemoteInstanceAsync(id); - // If we got this far, something failed, redisplay form - return View(model); + if (remoteInstance == null) + { + return NotFound(); } - public async Task Edit(string id) + var model = new EditRemoteInstanceViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + Id = remoteInstance.Id, + Name = remoteInstance.Name, + ClientName = remoteInstance.ClientName, + ApiKey = remoteInstance.ApiKey, + Url = remoteInstance.Url + }; + + return View(model); + } - var remoteInstance = await _service.GetRemoteInstanceAsync(id); + [HttpPost] + public async Task Edit(EditRemoteInstanceViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); + } - if (remoteInstance == null) - { - return NotFound(); - } + var remoteInstance = await _service.LoadRemoteInstanceAsync(model.Id); - var model = new EditRemoteInstanceViewModel - { - Id = remoteInstance.Id, - Name = remoteInstance.Name, - ClientName = remoteInstance.ClientName, - ApiKey = remoteInstance.ApiKey, - Url = remoteInstance.Url - }; - - return View(model); + if (remoteInstance == null) + { + return NotFound(); } - [HttpPost] - public async Task Edit(EditRemoteInstanceViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } - - var remoteInstance = await _service.LoadRemoteInstanceAsync(model.Id); - - if (remoteInstance == null) - { - return NotFound(); - } + ValidateViewModel(model); + } - if (ModelState.IsValid) - { - ValidateViewModel(model); - } + if (ModelState.IsValid) + { + await _service.UpdateRemoteInstance(model.Id, model.Name, model.Url, model.ClientName, model.ApiKey); - if (ModelState.IsValid) - { - await _service.UpdateRemoteInstance(model.Id, model.Name, model.Url, model.ClientName, model.ApiKey); + await _notifier.SuccessAsync(H["Remote instance updated successfully."]); - await _notifier.SuccessAsync(H["Remote instance updated successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); - } + // If we got this far, something failed, redisplay form + return View(model); + } - // If we got this far, something failed, redisplay form - return View(model); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string id) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + var remoteInstance = await _service.LoadRemoteInstanceAsync(id); - var remoteInstance = await _service.LoadRemoteInstanceAsync(id); + if (remoteInstance == null) + { + return NotFound(); + } - if (remoteInstance == null) - { - return NotFound(); - } + await _service.DeleteRemoteInstanceAsync(id); - await _service.DeleteRemoteInstanceAsync(id); + await _notifier.SuccessAsync(H["Remote instance deleted successfully."]); - await _notifier.SuccessAsync(H["Remote instance deleted successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ViewModels.ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) + { + return Forbid(); } - [HttpPost, ActionName("Index")] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ViewModels.ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageRemoteInstances)) - { - return Forbid(); - } + var remoteInstances = (await _service.LoadRemoteInstanceListAsync()).RemoteInstances; + var checkedContentItems = remoteInstances.Where(x => itemIds.Contains(x.Id)).ToList(); - if (itemIds?.Count() > 0) + switch (options.BulkAction) { - var remoteInstances = (await _service.LoadRemoteInstanceListAsync()).RemoteInstances; - var checkedContentItems = remoteInstances.Where(x => itemIds.Contains(x.Id)).ToList(); - - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _service.DeleteRemoteInstanceAsync(item.Id); - } - await _notifier.SuccessAsync(H["Remote instances successfully removed."]); - break; - default: - return BadRequest(); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _service.DeleteRemoteInstanceAsync(item.Id); + } + await _notifier.SuccessAsync(H["Remote instances successfully removed."]); + break; + default: + return BadRequest(); } - - return RedirectToAction(nameof(Index)); } - private void ValidateViewModel(EditRemoteInstanceViewModel model) + return RedirectToAction(nameof(Index)); + } + + private void ValidateViewModel(EditRemoteInstanceViewModel model) + { + if (string.IsNullOrWhiteSpace(model.Name)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Name), S["The name is mandatory."]); - } + ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Name), S["The name is mandatory."]); + } - if (string.IsNullOrWhiteSpace(model.ClientName)) - { - ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.ClientName), S["The client name is mandatory."]); - } + if (string.IsNullOrWhiteSpace(model.ClientName)) + { + ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.ClientName), S["The client name is mandatory."]); + } - if (string.IsNullOrWhiteSpace(model.ApiKey)) - { - ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.ApiKey), S["The api key is mandatory."]); - } + if (string.IsNullOrWhiteSpace(model.ApiKey)) + { + ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.ApiKey), S["The api key is mandatory."]); + } - if (string.IsNullOrWhiteSpace(model.Url)) - { - ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Url), S["The url is mandatory."]); - } - else + if (string.IsNullOrWhiteSpace(model.Url)) + { + ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Url), S["The url is mandatory."]); + } + else + { + if (!Uri.TryCreate(model.Url, UriKind.Absolute, out _)) { - if (!Uri.TryCreate(model.Url, UriKind.Absolute, out _)) - { - ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Url), S["The url is invalid."]); - } + ModelState.AddModelError(nameof(EditRemoteInstanceViewModel.Url), S["The url is invalid."]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClient.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClient.cs index 802c052119d..363c213b1e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClient.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClient.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Deployment.Remote.Models +namespace OrchardCore.Deployment.Remote.Models; + +public class RemoteClient { - public class RemoteClient - { - public string Id { get; set; } - public string ClientName { get; set; } - public byte[] ProtectedApiKey { get; set; } - } + public string Id { get; set; } + public string ClientName { get; set; } + public byte[] ProtectedApiKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClientList.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClientList.cs index 90a9fdc62a7..5d6ecffadec 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClientList.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteClientList.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace OrchardCore.Deployment.Remote.Models +namespace OrchardCore.Deployment.Remote.Models; + +public class RemoteClientList { - public class RemoteClientList - { - public List RemoteClients { get; set; } = []; - } + public List RemoteClients { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstance.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstance.cs index d6f450510fc..ee6915c7c97 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstance.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstance.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Deployment.Remote.Models +namespace OrchardCore.Deployment.Remote.Models; + +public class RemoteInstance { - public class RemoteInstance - { - public string Id { get; set; } - public string Name { get; set; } - public string ClientName { get; set; } - public string Url { get; set; } - public string ApiKey { get; set; } - } + public string Id { get; set; } + public string Name { get; set; } + public string ClientName { get; set; } + public string Url { get; set; } + public string ApiKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstanceList.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstanceList.cs index aa723484215..a1b1731f8bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstanceList.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Models/RemoteInstanceList.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Deployment.Remote.Models +namespace OrchardCore.Deployment.Remote.Models; + +public class RemoteInstanceList : Document { - public class RemoteInstanceList : Document - { - public List RemoteInstances { get; set; } = []; - } + public List RemoteInstances { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteClientService.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteClientService.cs index a3ecfc562f0..8695782d5f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteClientService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteClientService.cs @@ -6,92 +6,91 @@ using OrchardCore.Deployment.Remote.Models; using YesSql; -namespace OrchardCore.Deployment.Remote.Services +namespace OrchardCore.Deployment.Remote.Services; + +public class RemoteClientService { - public class RemoteClientService - { - private readonly IDataProtector _dataProtector; - private readonly ISession _session; + private readonly IDataProtector _dataProtector; + private readonly ISession _session; - private RemoteClientList _remoteClientList; + private RemoteClientList _remoteClientList; + + public RemoteClientService( + ISession session, + IDataProtectionProvider dataProtectionProvider + ) + { + _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); + _session = session; + } - public RemoteClientService( - ISession session, - IDataProtectionProvider dataProtectionProvider - ) + public async Task GetRemoteClientListAsync() + { + if (_remoteClientList != null) { - _dataProtector = dataProtectionProvider.CreateProtector("OrchardCore.Deployment").ToTimeLimitedDataProtector(); - _session = session; + return _remoteClientList; } - public async Task GetRemoteClientListAsync() + _remoteClientList = await _session.Query().FirstOrDefaultAsync(); + + if (_remoteClientList == null) { - if (_remoteClientList != null) - { - return _remoteClientList; - } + _remoteClientList = new RemoteClientList(); + await _session.SaveAsync(_remoteClientList); + } - _remoteClientList = await _session.Query().FirstOrDefaultAsync(); + return _remoteClientList; + } - if (_remoteClientList == null) - { - _remoteClientList = new RemoteClientList(); - await _session.SaveAsync(_remoteClientList); - } + public async Task GetRemoteClientAsync(string id) + { + var remoteClientList = await GetRemoteClientListAsync(); + return remoteClientList.RemoteClients.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + } - return _remoteClientList; - } + public async Task DeleteRemoteClientAsync(string id) + { + var remoteClientList = await GetRemoteClientListAsync(); + var remoteClient = await GetRemoteClientAsync(id); - public async Task GetRemoteClientAsync(string id) + if (remoteClient != null) { - var remoteClientList = await GetRemoteClientListAsync(); - return remoteClientList.RemoteClients.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + remoteClientList.RemoteClients.Remove(remoteClient); + await _session.SaveAsync(remoteClientList); } + } - public async Task DeleteRemoteClientAsync(string id) - { - var remoteClientList = await GetRemoteClientListAsync(); - var remoteClient = await GetRemoteClientAsync(id); - - if (remoteClient != null) - { - remoteClientList.RemoteClients.Remove(remoteClient); - await _session.SaveAsync(remoteClientList); - } - } + public async Task CreateRemoteClientAsync(string clientName, string apiKey) + { + var remoteClientList = await GetRemoteClientListAsync(); - public async Task CreateRemoteClientAsync(string clientName, string apiKey) + var remoteClient = new RemoteClient { - var remoteClientList = await GetRemoteClientListAsync(); + Id = Guid.NewGuid().ToString("n"), + ClientName = clientName, + ProtectedApiKey = _dataProtector.Protect(Encoding.UTF8.GetBytes(apiKey)), + }; - var remoteClient = new RemoteClient - { - Id = Guid.NewGuid().ToString("n"), - ClientName = clientName, - ProtectedApiKey = _dataProtector.Protect(Encoding.UTF8.GetBytes(apiKey)), - }; + remoteClientList.RemoteClients.Add(remoteClient); + await _session.SaveAsync(remoteClientList); - remoteClientList.RemoteClients.Add(remoteClient); - await _session.SaveAsync(remoteClientList); + return remoteClient; + } - return remoteClient; - } + public async Task TryUpdateRemoteClient(string id, string clientName, string apiKey) + { + var remoteClient = await GetRemoteClientAsync(id); - public async Task TryUpdateRemoteClient(string id, string clientName, string apiKey) + if (remoteClient == null) { - var remoteClient = await GetRemoteClientAsync(id); - - if (remoteClient == null) - { - return false; - } + return false; + } - remoteClient.ClientName = clientName; - remoteClient.ProtectedApiKey = _dataProtector.Protect(Encoding.UTF8.GetBytes(apiKey)); + remoteClient.ClientName = clientName; + remoteClient.ProtectedApiKey = _dataProtector.Protect(Encoding.UTF8.GetBytes(apiKey)); - await _session.SaveAsync(_remoteClientList); + await _session.SaveAsync(_remoteClientList); - return true; - } + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceDeploymentTargetProvider.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceDeploymentTargetProvider.cs index 24d5d0bd9b9..002f9d57c90 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceDeploymentTargetProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceDeploymentTargetProvider.cs @@ -5,38 +5,37 @@ using Microsoft.Extensions.Localization; using OrchardCore.Deployment.Remote.Services; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public class RemoteInstanceDeploymentTargetProvider : IDeploymentTargetProvider { - public class RemoteInstanceDeploymentTargetProvider : IDeploymentTargetProvider - { - private readonly RemoteInstanceService _service; - protected readonly IStringLocalizer S; + private readonly RemoteInstanceService _service; + protected readonly IStringLocalizer S; - public RemoteInstanceDeploymentTargetProvider( - IStringLocalizer stringLocalizer, - RemoteInstanceService service) - { - _service = service; - S = stringLocalizer; - } + public RemoteInstanceDeploymentTargetProvider( + IStringLocalizer stringLocalizer, + RemoteInstanceService service) + { + _service = service; + S = stringLocalizer; + } - public async Task> GetDeploymentTargetsAsync() - { - var remoteInstanceList = await _service.GetRemoteInstanceListAsync(); + public async Task> GetDeploymentTargetsAsync() + { + var remoteInstanceList = await _service.GetRemoteInstanceListAsync(); - return remoteInstanceList.RemoteInstances.Select(x => - new DeploymentTarget( - name: new LocalizedString(x.Name, x.Name, false), - description: S["Sends the deployment plan to a remote instance."], - route: new RouteValueDictionary(new - { - area = "OrchardCore.Deployment.Remote", - controller = "ExportRemoteInstance", - action = "Execute", - remoteInstanceId = x.Id - }) - ) - ).ToArray(); - } + return remoteInstanceList.RemoteInstances.Select(x => + new DeploymentTarget( + name: new LocalizedString(x.Name, x.Name, false), + description: S["Sends the deployment plan to a remote instance."], + route: new RouteValueDictionary(new + { + area = "OrchardCore.Deployment.Remote", + controller = "ExportRemoteInstance", + action = "Execute", + remoteInstanceId = x.Id + }) + ) + ).ToArray(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceService.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceService.cs index d81c968dd18..e36d4388c4f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Services/RemoteInstanceService.cs @@ -4,78 +4,77 @@ using OrchardCore.Deployment.Remote.Models; using OrchardCore.Documents; -namespace OrchardCore.Deployment.Remote.Services +namespace OrchardCore.Deployment.Remote.Services; + +public class RemoteInstanceService { - public class RemoteInstanceService - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public RemoteInstanceService(IDocumentManager documentManager) => _documentManager = documentManager; + public RemoteInstanceService(IDocumentManager documentManager) => _documentManager = documentManager; - /// - /// Loads the remote instances document from the store for updating and that should not be cached. - /// - public Task LoadRemoteInstanceListAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Loads the remote instances document from the store for updating and that should not be cached. + /// + public Task LoadRemoteInstanceListAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - /// Gets the remote instances document from the cache for sharing and that should not be updated. - /// - public Task GetRemoteInstanceListAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + /// Gets the remote instances document from the cache for sharing and that should not be updated. + /// + public Task GetRemoteInstanceListAsync() => _documentManager.GetOrCreateImmutableAsync(); - public async Task LoadRemoteInstanceAsync(string id) - { - var remoteInstanceList = await LoadRemoteInstanceListAsync(); - return FindRemoteInstance(remoteInstanceList, id); - } + public async Task LoadRemoteInstanceAsync(string id) + { + var remoteInstanceList = await LoadRemoteInstanceListAsync(); + return FindRemoteInstance(remoteInstanceList, id); + } - public async Task GetRemoteInstanceAsync(string id) - { - var remoteInstanceList = await GetRemoteInstanceListAsync(); - return FindRemoteInstance(remoteInstanceList, id); - } + public async Task GetRemoteInstanceAsync(string id) + { + var remoteInstanceList = await GetRemoteInstanceListAsync(); + return FindRemoteInstance(remoteInstanceList, id); + } - public async Task DeleteRemoteInstanceAsync(string id) - { - var remoteInstanceList = await LoadRemoteInstanceListAsync(); - var remoteInstance = FindRemoteInstance(remoteInstanceList, id); - - if (remoteInstance != null) - { - remoteInstanceList.RemoteInstances.Remove(remoteInstance); - await _documentManager.UpdateAsync(remoteInstanceList); - } - } + public async Task DeleteRemoteInstanceAsync(string id) + { + var remoteInstanceList = await LoadRemoteInstanceListAsync(); + var remoteInstance = FindRemoteInstance(remoteInstanceList, id); - public async Task CreateRemoteInstanceAsync(string name, string url, string clientName, string apiKey) + if (remoteInstance != null) { - var remoteInstanceList = await LoadRemoteInstanceListAsync(); - - remoteInstanceList.RemoteInstances.Add(new RemoteInstance - { - Id = Guid.NewGuid().ToString("n"), - Name = name, - Url = url, - ClientName = clientName, - ApiKey = apiKey, - }); - + remoteInstanceList.RemoteInstances.Remove(remoteInstance); await _documentManager.UpdateAsync(remoteInstanceList); } + } + + public async Task CreateRemoteInstanceAsync(string name, string url, string clientName, string apiKey) + { + var remoteInstanceList = await LoadRemoteInstanceListAsync(); - public async Task UpdateRemoteInstance(string id, string name, string url, string clientName, string apiKey) + remoteInstanceList.RemoteInstances.Add(new RemoteInstance { - var remoteInstanceList = await LoadRemoteInstanceListAsync(); - var remoteInstance = FindRemoteInstance(remoteInstanceList, id); + Id = Guid.NewGuid().ToString("n"), + Name = name, + Url = url, + ClientName = clientName, + ApiKey = apiKey, + }); - remoteInstance.Name = name; - remoteInstance.Url = url; - remoteInstance.ClientName = clientName; - remoteInstance.ApiKey = apiKey; + await _documentManager.UpdateAsync(remoteInstanceList); + } - await _documentManager.UpdateAsync(remoteInstanceList); - } + public async Task UpdateRemoteInstance(string id, string name, string url, string clientName, string apiKey) + { + var remoteInstanceList = await LoadRemoteInstanceListAsync(); + var remoteInstance = FindRemoteInstance(remoteInstanceList, id); + + remoteInstance.Name = name; + remoteInstance.Url = url; + remoteInstance.ClientName = clientName; + remoteInstance.ApiKey = apiKey; - private static RemoteInstance FindRemoteInstance(RemoteInstanceList remoteInstanceList, string id) => - remoteInstanceList.RemoteInstances.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + await _documentManager.UpdateAsync(remoteInstanceList); } + + private static RemoteInstance FindRemoteInstance(RemoteInstanceList remoteInstanceList, string id) => + remoteInstanceList.RemoteInstances.FirstOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Startup.cs index 04eca6a7c98..2627bd02ee9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/Startup.cs @@ -5,19 +5,18 @@ using OrchardCore.Navigation; using OrchardCore.Security.Permissions; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddHttpClient(); + services.AddHttpClient(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteClientViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteClientViewModel.cs index a7d705f0aec..6722a296a43 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteClientViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteClientViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Deployment.Remote.ViewModels +namespace OrchardCore.Deployment.Remote.ViewModels; + +public class EditRemoteClientViewModel { - public class EditRemoteClientViewModel - { - public string Id { get; set; } - public string ClientName { get; set; } - public string ApiKey { get; set; } - } + public string Id { get; set; } + public string ClientName { get; set; } + public string ApiKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteInstanceViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteInstanceViewModel.cs index 603320b2dee..3dc59f7402f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteInstanceViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/EditRemoteInstanceViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Deployment.Remote.ViewModels +namespace OrchardCore.Deployment.Remote.ViewModels; + +public class EditRemoteInstanceViewModel { - public class EditRemoteInstanceViewModel - { - public string Id { get; set; } - public string Name { get; set; } - public string ClientName { get; set; } - public string ApiKey { get; set; } - public string Url { get; set; } - } + public string Id { get; set; } + public string Name { get; set; } + public string ClientName { get; set; } + public string ApiKey { get; set; } + public string Url { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/ImportViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/ImportViewModel.cs index e9313fd3bed..dd78d1d0a3d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/ImportViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/ImportViewModel.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Deployment.Remote.ViewModels +namespace OrchardCore.Deployment.Remote.ViewModels; + +public class ImportViewModel { - public class ImportViewModel - { - public string ClientName { get; set; } - public string ApiKey { get; set; } - public IFormFile Content { get; set; } - } + public string ClientName { get; set; } + public string ApiKey { get; set; } + public IFormFile Content { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteClientIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteClientIndexViewModel.cs index b7b57a8c0b6..909cbb9aee1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteClientIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteClientIndexViewModel.cs @@ -3,35 +3,34 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Deployment.Remote.Models; -namespace OrchardCore.Deployment.Remote.ViewModels +namespace OrchardCore.Deployment.Remote.ViewModels; + +public class RemoteClientIndexViewModel { - public class RemoteClientIndexViewModel - { - public List RemoteClients { get; set; } + public List RemoteClients { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); + public ContentOptions Options { get; set; } = new ContentOptions(); - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } +} - public class ContentOptions - { - public ContentsBulkAction BulkAction { get; set; } +public class ContentOptions +{ + public ContentsBulkAction BulkAction { get; set; } - public string Search { get; set; } + public string Search { get; set; } - #region Lists to populate + #region Lists to populate - [BindNever] - public List ContentsBulkAction { get; set; } + [BindNever] + public List ContentsBulkAction { get; set; } - #endregion Lists to populate - } + #endregion Lists to populate +} - public enum ContentsBulkAction - { - None, - Remove - } +public enum ContentsBulkAction +{ + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteInstanceIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteInstanceIndexViewModel.cs index 03edb549759..41bf88998f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteInstanceIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment.Remote/ViewModels/RemoteInstanceIndexViewModel.cs @@ -2,15 +2,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Deployment.Remote.Models; -namespace OrchardCore.Deployment.Remote.ViewModels +namespace OrchardCore.Deployment.Remote.ViewModels; + +public class RemoteInstanceIndexViewModel { - public class RemoteInstanceIndexViewModel - { - public List RemoteInstances { get; set; } + public List RemoteInstances { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); + public ContentOptions Options { get; set; } = new ContentOptions(); - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/AdminMenu.cs index 9a28238c78a..3a4ab4fe76e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/AdminMenu.cs @@ -2,46 +2,45 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Import/Export"], S["Import/Export"].PrefixPosition(), import => import - .Add(S["Deployment Plans"], S["Deployment Plans"].PrefixPosition(), deployment => deployment - .Action("Index", "DeploymentPlan", "OrchardCore.Deployment") - .Permission(CommonPermissions.Export) - .LocalNav() - ) - .Add(S["Package Import"], S["Package Import"].PrefixPosition(), deployment => deployment - .Action("Index", "Import", "OrchardCore.Deployment") - .Permission(CommonPermissions.Import) - .LocalNav() - ) - .Add(S["JSON Import"], S["JSON Import"].PrefixPosition(), deployment => deployment - .Action("Json", "Import", "OrchardCore.Deployment") - .Permission(CommonPermissions.Import) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Import/Export"], S["Import/Export"].PrefixPosition(), import => import + .Add(S["Deployment Plans"], S["Deployment Plans"].PrefixPosition(), deployment => deployment + .Action("Index", "DeploymentPlan", "OrchardCore.Deployment") + .Permission(CommonPermissions.Export) + .LocalNav() + ) + .Add(S["Package Import"], S["Package Import"].PrefixPosition(), deployment => deployment + .Action("Index", "Import", "OrchardCore.Deployment") + .Permission(CommonPermissions.Import) + .LocalNav() + ) + .Add(S["JSON Import"], S["JSON Import"].PrefixPosition(), deployment => deployment + .Action("Json", "Import", "OrchardCore.Deployment") + .Permission(CommonPermissions.Import) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/DeploymentPlanController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/DeploymentPlanController.cs index 3556a0f7edc..2010d01cc0a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/DeploymentPlanController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/DeploymentPlanController.cs @@ -20,319 +20,318 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Deployment.Controllers +namespace OrchardCore.Deployment.Controllers; + +[Admin("DeploymentPlan/{action}/{id?}", "DeploymentPlan{action}")] +public class DeploymentPlanController : Controller { - [Admin("DeploymentPlan/{action}/{id?}", "DeploymentPlan{action}")] - public class DeploymentPlanController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _factories; + private readonly ISession _session; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IShapeFactory _shapeFactory; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public DeploymentPlanController( + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable factories, + ISession session, + IOptions pagerOptions, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IUpdateModelAccessor updateModelAccessor) + { + _displayManager = displayManager; + _factories = factories; + _authorizationService = authorizationService; + _session = session; + _pagerOptions = pagerOptions.Value; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + } + + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _factories; - private readonly ISession _session; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IShapeFactory _shapeFactory; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public DeploymentPlanController( - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable factories, - ISession session, - IOptions pagerOptions, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IUpdateModelAccessor updateModelAccessor) + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - _displayManager = displayManager; - _factories = factories; - _authorizationService = authorizationService; - _session = session; - _pagerOptions = pagerOptions.Value; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - public async Task Index(ContentOptions options, PagerParameters pagerParameters) + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Export)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Export)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var deploymentPlans = _session.Query(); - var deploymentPlans = _session.Query(); + if (!string.IsNullOrWhiteSpace(options.Search)) + { + deploymentPlans = deploymentPlans.Where(x => x.Name.Contains(options.Search)); + } - if (!string.IsNullOrWhiteSpace(options.Search)) - { - deploymentPlans = deploymentPlans.Where(x => x.Name.Contains(options.Search)); - } + var count = await deploymentPlans.CountAsync(); - var count = await deploymentPlans.CountAsync(); + var results = await deploymentPlans + .OrderBy(p => p.Name) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ListAsync(); - var results = await deploymentPlans - .OrderBy(p => p.Name) - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ListAsync(); + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var model = new DeploymentPlanIndexViewModel + { + DeploymentPlans = results.Select(x => new DeploymentPlanEntry { DeploymentPlan = x }).ToList(), + Options = options, + Pager = pagerShape + }; - var model = new DeploymentPlanIndexViewModel - { - DeploymentPlans = results.Select(x => new DeploymentPlanEntry { DeploymentPlan = x }).ToList(), - Options = options, - Pager = pagerShape - }; + model.Options.DeploymentPlansBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Delete)), + ]; - model.Options.DeploymentPlansBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Delete)), - ]; + return View(model); + } - return View(model); - } + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(DeploymentPlanIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(DeploymentPlanIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexBulkActionPOST(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + { + return Forbid(); + } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexBulkActionPOST(ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + var checkedItems = await _session.Query().Where(x => x.DocumentId.IsIn(itemIds)).ListAsync(); + switch (options.BulkAction) { - return Forbid(); + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Delete: + foreach (var item in checkedItems) + { + _session.Delete(item); + } + await _notifier.SuccessAsync(H["Deployment plans successfully deleted."]); + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } + } - if (itemIds?.Count() > 0) - { - var checkedItems = await _session.Query().Where(x => x.DocumentId.IsIn(itemIds)).ListAsync(); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Delete: - foreach (var item in checkedItems) - { - _session.Delete(item); - } - await _notifier.SuccessAsync(H["Deployment plans successfully deleted."]); - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } - } + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + public async Task Display(long id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + { + return Forbid(); } - public async Task Display(long id) - { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + var deploymentPlan = await _session.GetAsync(id); - var deploymentPlan = await _session.GetAsync(id); + if (deploymentPlan == null) + { + return NotFound(); + } - if (deploymentPlan == null) - { - return NotFound(); - } + var items = new List(); + foreach (var step in deploymentPlan.DeploymentSteps) + { + var item = await _displayManager.BuildDisplayAsync(step, _updateModelAccessor.ModelUpdater, "Summary"); + item.Properties["DeploymentStep"] = step; + items.Add(item); + } - var items = new List(); - foreach (var step in deploymentPlan.DeploymentSteps) - { - var item = await _displayManager.BuildDisplayAsync(step, _updateModelAccessor.ModelUpdater, "Summary"); - item.Properties["DeploymentStep"] = step; - items.Add(item); - } + var thumbnails = new Dictionary(); + foreach (var factory in _factories) + { + var step = factory.Create(); + var thumbnail = await _displayManager.BuildDisplayAsync(step, _updateModelAccessor.ModelUpdater, "Thumbnail"); + thumbnail.Properties["DeploymentStep"] = step; + thumbnails.Add(factory.Name, thumbnail); + } - var thumbnails = new Dictionary(); - foreach (var factory in _factories) - { - var step = factory.Create(); - var thumbnail = await _displayManager.BuildDisplayAsync(step, _updateModelAccessor.ModelUpdater, "Thumbnail"); - thumbnail.Properties["DeploymentStep"] = step; - thumbnails.Add(factory.Name, thumbnail); - } + var model = new DisplayDeploymentPlanViewModel + { + DeploymentPlan = deploymentPlan, + Items = items, + Thumbnails = thumbnails, + }; - var model = new DisplayDeploymentPlanViewModel - { - DeploymentPlan = deploymentPlan, - Items = items, - Thumbnails = thumbnails, - }; + return View(model); + } - return View(model); + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + { + return Forbid(); } - public async Task Create() - { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + var model = new CreateDeploymentPlanViewModel(); - var model = new CreateDeploymentPlanViewModel(); + return View(model); + } - return View(model); + [HttpPost] + public async Task Create(CreateDeploymentPlanViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + { + return Forbid(); } - [HttpPost] - public async Task Create(CreateDeploymentPlanViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["The name is mandatory."]); } - if (ModelState.IsValid) + var count = await _session.QueryIndex(x => x.Name == model.Name).CountAsync(); + if (count > 0) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["The name is mandatory."]); - } - - var count = await _session.QueryIndex(x => x.Name == model.Name).CountAsync(); - if (count > 0) - { - ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["A deployment plan with the same name already exists."]); - } + ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["A deployment plan with the same name already exists."]); } + } - if (ModelState.IsValid) - { - var deploymentPlan = new DeploymentPlan { Name = model.Name }; - - await _session.SaveAsync(deploymentPlan); + if (ModelState.IsValid) + { + var deploymentPlan = new DeploymentPlan { Name = model.Name }; - return RedirectToAction(nameof(Index)); - } + await _session.SaveAsync(deploymentPlan); - // If we got this far, something failed, redisplay form - return View(model); + return RedirectToAction(nameof(Index)); } - public async Task Edit(long id) + // If we got this far, something failed, redisplay form + return View(model); + } + + public async Task Edit(long id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var model = new EditDeploymentPlanViewModel - { - Id = deploymentPlan.Id, - Name = deploymentPlan.Name - }; + var model = new EditDeploymentPlanViewModel + { + Id = deploymentPlan.Id, + Name = deploymentPlan.Name + }; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Edit(EditDeploymentPlanViewModel model) + [HttpPost] + public async Task Edit(EditDeploymentPlanViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } + + var deploymentPlan = await _session.GetAsync(model.Id); - var deploymentPlan = await _session.GetAsync(model.Id); + if (deploymentPlan == null) + { + return NotFound(); + } - if (deploymentPlan == null) + if (ModelState.IsValid) + { + if (string.IsNullOrWhiteSpace(model.Name)) { - return NotFound(); + ModelState.AddModelError(nameof(EditDeploymentPlanViewModel.Name), S["The name is mandatory."]); } - - if (ModelState.IsValid) + if (!string.Equals(model.Name, deploymentPlan.Name, StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(EditDeploymentPlanViewModel.Name), S["The name is mandatory."]); - } - if (!string.Equals(model.Name, deploymentPlan.Name, StringComparison.OrdinalIgnoreCase)) + var count = await _session.QueryIndex(x => x.Name == model.Name && x.DocumentId != model.Id).CountAsync(); + if (count > 0) { - var count = await _session.QueryIndex(x => x.Name == model.Name && x.DocumentId != model.Id).CountAsync(); - if (count > 0) - { - ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["A deployment plan with the same name already exists."]); - } + ModelState.AddModelError(nameof(CreateDeploymentPlanViewModel.Name), S["A deployment plan with the same name already exists."]); } } + } - if (ModelState.IsValid) - { - deploymentPlan.Name = model.Name; - - await _session.SaveAsync(deploymentPlan); + if (ModelState.IsValid) + { + deploymentPlan.Name = model.Name; - await _notifier.SuccessAsync(H["Deployment plan updated successfully."]); + await _session.SaveAsync(deploymentPlan); - return RedirectToAction(nameof(Index)); - } + await _notifier.SuccessAsync(H["Deployment plan updated successfully."]); - // If we got this far, something failed, redisplay form - return View(model); + return RedirectToAction(nameof(Index)); } - [HttpPost] - public async Task Delete(long id) + // If we got this far, something failed, redisplay form + return View(model); + } + + [HttpPost] + public async Task Delete(long id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - _session.Delete(deploymentPlan); + _session.Delete(deploymentPlan); - await _notifier.SuccessAsync(H["Deployment plan deleted successfully."]); + await _notifier.SuccessAsync(H["Deployment plan deleted successfully."]); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ExportFileController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ExportFileController.cs index b2cfaa3f843..f7bf9a81a1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ExportFileController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ExportFileController.cs @@ -13,70 +13,69 @@ using OrchardCore.Recipes.Models; using YesSql; -namespace OrchardCore.Deployment.Controllers +namespace OrchardCore.Deployment.Controllers; + +[Admin("DeploymentPlan/ExportFile/{action}/{id?}", "DeploymentPlanExportFile{action}")] +public class ExportFileController : Controller { - [Admin("DeploymentPlan/ExportFile/{action}/{id?}", "DeploymentPlanExportFile{action}")] - public class ExportFileController : Controller + private readonly IDeploymentManager _deploymentManager; + private readonly IAuthorizationService _authorizationService; + private readonly ISession _session; + + public ExportFileController( + IAuthorizationService authorizationService, + ISession session, + IDeploymentManager deploymentManager) { - private readonly IDeploymentManager _deploymentManager; - private readonly IAuthorizationService _authorizationService; - private readonly ISession _session; + _authorizationService = authorizationService; + _deploymentManager = deploymentManager; + _session = session; + } - public ExportFileController( - IAuthorizationService authorizationService, - ISession session, - IDeploymentManager deploymentManager) + [HttpPost] + [DeleteFileResultFilter] + public async Task Execute(long id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Export)) { - _authorizationService = authorizationService; - _deploymentManager = deploymentManager; - _session = session; + return Forbid(); } - [HttpPost] - [DeleteFileResultFilter] - public async Task Execute(long id) + var deploymentPlan = await _session.GetAsync(id); + + if (deploymentPlan == null) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Export)) - { - return Forbid(); - } + return NotFound(); + } - var deploymentPlan = await _session.GetAsync(id); + string archiveFileName; + var filename = deploymentPlan.Name.ToSafeName() + ".zip"; - if (deploymentPlan == null) - { - return NotFound(); - } + using (var fileBuilder = new TemporaryFileBuilder()) + { + archiveFileName = fileBuilder.Folder + ".zip"; - string archiveFileName; - var filename = deploymentPlan.Name.ToSafeName() + ".zip"; + var recipeDescriptor = new RecipeDescriptor(); + var recipeFileDeploymentStep = deploymentPlan.DeploymentSteps.FirstOrDefault(ds => ds.Name == nameof(RecipeFileDeploymentStep)) as RecipeFileDeploymentStep; - using (var fileBuilder = new TemporaryFileBuilder()) + if (recipeFileDeploymentStep != null) { - archiveFileName = fileBuilder.Folder + ".zip"; - - var recipeDescriptor = new RecipeDescriptor(); - var recipeFileDeploymentStep = deploymentPlan.DeploymentSteps.FirstOrDefault(ds => ds.Name == nameof(RecipeFileDeploymentStep)) as RecipeFileDeploymentStep; - - if (recipeFileDeploymentStep != null) - { - recipeDescriptor.Name = recipeFileDeploymentStep.RecipeName; - recipeDescriptor.DisplayName = recipeFileDeploymentStep.DisplayName; - recipeDescriptor.Description = recipeFileDeploymentStep.Description; - recipeDescriptor.Author = recipeFileDeploymentStep.Author; - recipeDescriptor.WebSite = recipeFileDeploymentStep.WebSite; - recipeDescriptor.Version = recipeFileDeploymentStep.Version; - recipeDescriptor.IsSetupRecipe = recipeFileDeploymentStep.IsSetupRecipe; - recipeDescriptor.Categories = (recipeFileDeploymentStep.Categories ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries); - recipeDescriptor.Tags = (recipeFileDeploymentStep.Tags ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries); - } - - var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, recipeDescriptor); - await _deploymentManager.ExecuteDeploymentPlanAsync(deploymentPlan, deploymentPlanResult); - ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName); + recipeDescriptor.Name = recipeFileDeploymentStep.RecipeName; + recipeDescriptor.DisplayName = recipeFileDeploymentStep.DisplayName; + recipeDescriptor.Description = recipeFileDeploymentStep.Description; + recipeDescriptor.Author = recipeFileDeploymentStep.Author; + recipeDescriptor.WebSite = recipeFileDeploymentStep.WebSite; + recipeDescriptor.Version = recipeFileDeploymentStep.Version; + recipeDescriptor.IsSetupRecipe = recipeFileDeploymentStep.IsSetupRecipe; + recipeDescriptor.Categories = (recipeFileDeploymentStep.Categories ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries); + recipeDescriptor.Tags = (recipeFileDeploymentStep.Tags ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries); } - return new PhysicalFileResult(archiveFileName, "application/zip") { FileDownloadName = filename }; + var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, recipeDescriptor); + await _deploymentManager.ExecuteDeploymentPlanAsync(deploymentPlan, deploymentPlanResult); + ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName); } + + return new PhysicalFileResult(archiveFileName, "application/zip") { FileDownloadName = filename }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ImportController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ImportController.cs index 8d794abc666..1194bb36f79 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ImportController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/ImportController.cs @@ -17,177 +17,176 @@ using OrchardCore.Mvc.Utilities; using OrchardCore.Recipes.Models; -namespace OrchardCore.Deployment.Controllers +namespace OrchardCore.Deployment.Controllers; + +[Admin("DeploymentPlan/Import/{action}", "DeploymentPlanImport{action}")] +public class ImportController : Controller { - [Admin("DeploymentPlan/Import/{action}", "DeploymentPlanImport{action}")] - public class ImportController : Controller + private readonly IDeploymentManager _deploymentManager; + private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + + public ImportController( + IDeploymentManager deploymentManager, + IAuthorizationService authorizationService, + INotifier notifier, + ILogger logger, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer + ) + { + _deploymentManager = deploymentManager; + _authorizationService = authorizationService; + _notifier = notifier; + _logger = logger; + H = htmlLocalizer; + S = stringLocalizer; + } + + public async Task Index() { - private readonly IDeploymentManager _deploymentManager; - private readonly IAuthorizationService _authorizationService; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - - public ImportController( - IDeploymentManager deploymentManager, - IAuthorizationService authorizationService, - INotifier notifier, - ILogger logger, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer - ) + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) { - _deploymentManager = deploymentManager; - _authorizationService = authorizationService; - _notifier = notifier; - _logger = logger; - H = htmlLocalizer; - S = stringLocalizer; + return Forbid(); } - public async Task Index() - { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) - { - return Forbid(); - } + return View(); + } - return View(); + [HttpPost] + public async Task Import(IFormFile importedPackage) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) + { + return Forbid(); } - [HttpPost] - public async Task Import(IFormFile importedPackage) + if (importedPackage != null) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) - { - return Forbid(); - } + var tempArchiveName = Path.GetTempFileName() + Path.GetExtension(importedPackage.FileName); + var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - if (importedPackage != null) + try { - var tempArchiveName = Path.GetTempFileName() + Path.GetExtension(importedPackage.FileName); - var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - - try + using (var stream = new FileStream(tempArchiveName, FileMode.Create)) { - using (var stream = new FileStream(tempArchiveName, FileMode.Create)) - { - await importedPackage.CopyToAsync(stream); - } - - if (importedPackage.FileName.EndsWith(".zip")) - { - ZipFile.ExtractToDirectory(tempArchiveName, tempArchiveFolder); - } - else if (importedPackage.FileName.EndsWith(".json")) - { - Directory.CreateDirectory(tempArchiveFolder); - System.IO.File.Move(tempArchiveName, Path.Combine(tempArchiveFolder, "Recipe.json")); - } - else - { - await _notifier.ErrorAsync(H["Only zip or json files are supported."]); - - return RedirectToAction(nameof(Index)); - } - - await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); - - await _notifier.SuccessAsync(H["Deployment package imported."]); + await importedPackage.CopyToAsync(stream); } - catch (RecipeExecutionException e) - { - _logger.LogError(e, "Unable to import a deployment package."); - await _notifier.ErrorAsync(H["The import failed with the following errors: {0}", string.Join(' ', e.StepResult.Errors)]); + if (importedPackage.FileName.EndsWith(".zip")) + { + ZipFile.ExtractToDirectory(tempArchiveName, tempArchiveFolder); } - catch (Exception e) + else if (importedPackage.FileName.EndsWith(".json")) { - _logger.LogError(e, "Unable to import a deployment package."); - - await _notifier.ErrorAsync(H["Unexpected error occurred while importing the deployment package."]); + Directory.CreateDirectory(tempArchiveFolder); + System.IO.File.Move(tempArchiveName, Path.Combine(tempArchiveFolder, "Recipe.json")); } - finally + else { - if (System.IO.File.Exists(tempArchiveName)) - { - System.IO.File.Delete(tempArchiveName); - } - - if (Directory.Exists(tempArchiveFolder)) - { - Directory.Delete(tempArchiveFolder, true); - } + await _notifier.ErrorAsync(H["Only zip or json files are supported."]); + + return RedirectToAction(nameof(Index)); } + + await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); + + await _notifier.SuccessAsync(H["Deployment package imported."]); } - else + catch (RecipeExecutionException e) { - await _notifier.ErrorAsync(H["Please add a file to import."]); + _logger.LogError(e, "Unable to import a deployment package."); + + await _notifier.ErrorAsync(H["The import failed with the following errors: {0}", string.Join(' ', e.StepResult.Errors)]); + } + catch (Exception e) + { + _logger.LogError(e, "Unable to import a deployment package."); + + await _notifier.ErrorAsync(H["Unexpected error occurred while importing the deployment package."]); } + finally + { + if (System.IO.File.Exists(tempArchiveName)) + { + System.IO.File.Delete(tempArchiveName); + } - return RedirectToAction(nameof(Index)); + if (Directory.Exists(tempArchiveFolder)) + { + Directory.Delete(tempArchiveFolder, true); + } + } + } + else + { + await _notifier.ErrorAsync(H["Please add a file to import."]); } - public async Task Json() + return RedirectToAction(nameof(Index)); + } + + public async Task Json() + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) - { - return Forbid(); - } + return Forbid(); + } + + return View(); + } - return View(); + [HttpPost] + public async Task Json(ImportJsonViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) + { + return Forbid(); } - [HttpPost] - public async Task Json(ImportJsonViewModel model) + if (!model.Json.IsJson(JOptions.Document)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.Import)) - { - return Forbid(); - } + ModelState.AddModelError(nameof(model.Json), S["The recipe is written in an incorrect JSON format."]); + } - if (!model.Json.IsJson(JOptions.Document)) - { - ModelState.AddModelError(nameof(model.Json), S["The recipe is written in an incorrect JSON format."]); - } + if (ModelState.IsValid) + { + var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - if (ModelState.IsValid) + try { - var tempArchiveFolder = PathExtensions.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempArchiveFolder); + System.IO.File.WriteAllText(Path.Combine(tempArchiveFolder, "Recipe.json"), model.Json); - try - { - Directory.CreateDirectory(tempArchiveFolder); - System.IO.File.WriteAllText(Path.Combine(tempArchiveFolder, "Recipe.json"), model.Json); + await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); - await _deploymentManager.ImportDeploymentPackageAsync(new PhysicalFileProvider(tempArchiveFolder)); - - await _notifier.SuccessAsync(H["Recipe imported successfully!"]); - } - catch (RecipeExecutionException e) - { - _logger.LogError(e, "Unable to import a recipe from JSON input."); + await _notifier.SuccessAsync(H["Recipe imported successfully!"]); + } + catch (RecipeExecutionException e) + { + _logger.LogError(e, "Unable to import a recipe from JSON input."); - ModelState.AddModelError(nameof(model.Json), string.Join(' ', e.StepResult.Errors)); - } - catch (Exception e) - { - _logger.LogError(e, "Unable to import a recipe from JSON input."); + ModelState.AddModelError(nameof(model.Json), string.Join(' ', e.StepResult.Errors)); + } + catch (Exception e) + { + _logger.LogError(e, "Unable to import a recipe from JSON input."); - ModelState.AddModelError(string.Empty, S["Unexpected error occurred while importing the recipe."]); - } - finally + ModelState.AddModelError(string.Empty, S["Unexpected error occurred while importing the recipe."]); + } + finally + { + if (Directory.Exists(tempArchiveFolder)) { - if (Directory.Exists(tempArchiveFolder)) - { - Directory.Delete(tempArchiveFolder, true); - } + Directory.Delete(tempArchiveFolder, true); } } - - return View(model); } + + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/StepController.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/StepController.cs index db7bc2f1326..40d35fc97fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/StepController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Controllers/StepController.cs @@ -12,252 +12,251 @@ using OrchardCore.DisplayManagement.Notify; using YesSql; -namespace OrchardCore.Deployment.Controllers +namespace OrchardCore.Deployment.Controllers; + +[Admin] +public class StepController : Controller { - [Admin] - public class StepController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _factories; + private readonly ISession _session; + private readonly INotifier _notifier; + private readonly IUpdateModelAccessor _updateModelAccessor; + + protected readonly IHtmlLocalizer H; + + public StepController( + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable factories, + ISession session, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IUpdateModelAccessor updateModelAccessor) { - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _factories; - private readonly ISession _session; - private readonly INotifier _notifier; - private readonly IUpdateModelAccessor _updateModelAccessor; - - protected readonly IHtmlLocalizer H; - - public StepController( - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable factories, - ISession session, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IUpdateModelAccessor updateModelAccessor) - { - _displayManager = displayManager; - _factories = factories; - _authorizationService = authorizationService; - _session = session; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - H = htmlLocalizer; - } + _displayManager = displayManager; + _factories = factories; + _authorizationService = authorizationService; + _session = session; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + H = htmlLocalizer; + } - [Admin("DeploymentPlan/{id}/Step/Create", "DeploymentPlanCreateStep")] - public async Task Create(long id, string type) + [Admin("DeploymentPlan/{id}/Step/Create", "DeploymentPlanCreateStep")] + public async Task Create(long id, string type) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = _factories.FirstOrDefault(x => x.Name == type)?.Create(); + var step = _factories.FirstOrDefault(x => x.Name == type)?.Create(); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - step.Id = Guid.NewGuid().ToString("n"); + step.Id = Guid.NewGuid().ToString("n"); - var model = new EditDeploymentPlanStepViewModel - { - DeploymentPlanId = id, - DeploymentStep = step, - DeploymentStepId = step.Id, - DeploymentStepType = type, - Editor = await _displayManager.BuildEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty) - }; + var model = new EditDeploymentPlanStepViewModel + { + DeploymentPlanId = id, + DeploymentStep = step, + DeploymentStepId = step.Id, + DeploymentStepType = type, + Editor = await _displayManager.BuildEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty) + }; - model.Editor.DeploymentStep = step; + model.Editor.DeploymentStep = step; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Create(EditDeploymentPlanStepViewModel model) + [HttpPost] + public async Task Create(EditDeploymentPlanStepViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(model.DeploymentPlanId); + var deploymentPlan = await _session.GetAsync(model.DeploymentPlanId); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = _factories.FirstOrDefault(x => x.Name == model.DeploymentStepType)?.Create(); + var step = _factories.FirstOrDefault(x => x.Name == model.DeploymentStepType)?.Create(); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); - editor.Properties["DeploymentStep"] = step; + var editor = await _displayManager.UpdateEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty); + editor.Properties["DeploymentStep"] = step; - if (ModelState.IsValid) - { - step.Id = model.DeploymentStepId; - deploymentPlan.DeploymentSteps.Add(step); - await _session.SaveAsync(deploymentPlan); + if (ModelState.IsValid) + { + step.Id = model.DeploymentStepId; + deploymentPlan.DeploymentSteps.Add(step); + await _session.SaveAsync(deploymentPlan); - await _notifier.SuccessAsync(H["Deployment plan step added successfully."]); - return RedirectToAction("Display", "DeploymentPlan", new { id = model.DeploymentPlanId }); - } + await _notifier.SuccessAsync(H["Deployment plan step added successfully."]); + return RedirectToAction("Display", "DeploymentPlan", new { id = model.DeploymentPlanId }); + } - model.Editor = editor; + model.Editor = editor; - // If we got this far, something failed, redisplay form - return View(model); - } + // If we got this far, something failed, redisplay form + return View(model); + } - [Admin("DeploymentPlan/{id}/Step/{stepId}/Edit", "DeploymentPlanEditStep")] - public async Task Edit(long id, string stepId) + [Admin("DeploymentPlan/{id}/Step/{stepId}/Edit", "DeploymentPlanEditStep")] + public async Task Edit(long id, string stepId) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, stepId, StringComparison.OrdinalIgnoreCase)); + var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, stepId, StringComparison.OrdinalIgnoreCase)); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - var model = new EditDeploymentPlanStepViewModel - { - DeploymentPlanId = id, - DeploymentStep = step, - DeploymentStepId = step.Id, - DeploymentStepType = step.GetType().Name, - Editor = await _displayManager.BuildEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") - }; + var model = new EditDeploymentPlanStepViewModel + { + DeploymentPlanId = id, + DeploymentStep = step, + DeploymentStepId = step.Id, + DeploymentStepType = step.GetType().Name, + Editor = await _displayManager.BuildEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") + }; - model.Editor.DeploymentStep = step; + model.Editor.DeploymentStep = step; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Edit(EditDeploymentPlanStepViewModel model) + [HttpPost] + public async Task Edit(EditDeploymentPlanStepViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(model.DeploymentPlanId); + var deploymentPlan = await _session.GetAsync(model.DeploymentPlanId); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, model.DeploymentStepId, StringComparison.OrdinalIgnoreCase)); + var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, model.DeploymentStepId, StringComparison.OrdinalIgnoreCase)); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var editor = await _displayManager.UpdateEditorAsync(step, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - if (ModelState.IsValid) - { - await _session.SaveAsync(deploymentPlan); + if (ModelState.IsValid) + { + await _session.SaveAsync(deploymentPlan); - await _notifier.SuccessAsync(H["Deployment plan step updated successfully."]); - return RedirectToAction("Display", "DeploymentPlan", new { id = model.DeploymentPlanId }); - } + await _notifier.SuccessAsync(H["Deployment plan step updated successfully."]); + return RedirectToAction("Display", "DeploymentPlan", new { id = model.DeploymentPlanId }); + } - await _notifier.ErrorAsync(H["The deployment plan step has validation errors."]); - model.Editor = editor; + await _notifier.ErrorAsync(H["The deployment plan step has validation errors."]); + model.Editor = editor; - // If we got this far, something failed, redisplay form - return View(model); - } + // If we got this far, something failed, redisplay form + return View(model); + } - [HttpPost] - [Admin("DeploymentPlan/{id}/Step/{stepId}/Delete", "DeploymentPlanDeleteStep")] - public async Task Delete(long id, string stepId) + [HttpPost] + [Admin("DeploymentPlan/{id}/Step/{stepId}/Delete", "DeploymentPlanDeleteStep")] + public async Task Delete(long id, string stepId) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, stepId, StringComparison.OrdinalIgnoreCase)); + var step = deploymentPlan.DeploymentSteps.FirstOrDefault(x => string.Equals(x.Id, stepId, StringComparison.OrdinalIgnoreCase)); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - deploymentPlan.DeploymentSteps.Remove(step); - await _session.SaveAsync(deploymentPlan); + deploymentPlan.DeploymentSteps.Remove(step); + await _session.SaveAsync(deploymentPlan); - await _notifier.SuccessAsync(H["Deployment step deleted successfully."]); + await _notifier.SuccessAsync(H["Deployment step deleted successfully."]); - return RedirectToAction("Display", "DeploymentPlan", new { id }); - } + return RedirectToAction("Display", "DeploymentPlan", new { id }); + } - [HttpPost] - public async Task UpdateOrder(long id, int oldIndex, int newIndex) + [HttpPost] + public async Task UpdateOrder(long id, int oldIndex, int newIndex) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageDeploymentPlan)) - { - return Forbid(); - } + return Forbid(); + } - var deploymentPlan = await _session.GetAsync(id); + var deploymentPlan = await _session.GetAsync(id); - if (deploymentPlan == null) - { - return NotFound(); - } + if (deploymentPlan == null) + { + return NotFound(); + } - var step = deploymentPlan.DeploymentSteps.ElementAtOrDefault(oldIndex); + var step = deploymentPlan.DeploymentSteps.ElementAtOrDefault(oldIndex); - if (step == null) - { - return NotFound(); - } + if (step == null) + { + return NotFound(); + } - deploymentPlan.DeploymentSteps.RemoveAt(oldIndex); + deploymentPlan.DeploymentSteps.RemoveAt(oldIndex); - deploymentPlan.DeploymentSteps.Insert(newIndex, step); + deploymentPlan.DeploymentSteps.Insert(newIndex, step); - await _session.SaveAsync(deploymentPlan); + await _session.SaveAsync(deploymentPlan); - return Ok(); - } + return Ok(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentSource.cs index 4ec1d3b4d29..ec754d84dfc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentSource.cs @@ -6,74 +6,73 @@ using Microsoft.Extensions.Options; using OrchardCore.Json; -namespace OrchardCore.Deployment.Deployment +namespace OrchardCore.Deployment.Deployment; + +public class DeploymentPlanDeploymentSource : IDeploymentSource { - public class DeploymentPlanDeploymentSource : IDeploymentSource + private readonly IDeploymentPlanService _deploymentPlanService; + private readonly IEnumerable _deploymentStepFactories; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public DeploymentPlanDeploymentSource( + IDeploymentPlanService deploymentPlanService, + IEnumerable deploymentStepFactories, + IOptions jsonSerializerOptions) { - private readonly IDeploymentPlanService _deploymentPlanService; - private readonly IEnumerable _deploymentStepFactories; - private readonly JsonSerializerOptions _jsonSerializerOptions; + _deploymentPlanService = deploymentPlanService; + _deploymentStepFactories = deploymentStepFactories; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + } - public DeploymentPlanDeploymentSource( - IDeploymentPlanService deploymentPlanService, - IEnumerable deploymentStepFactories, - IOptions jsonSerializerOptions) + public async Task ProcessDeploymentStepAsync(DeploymentStep deploymentStep, DeploymentPlanResult result) + { + if (deploymentStep is not DeploymentPlanDeploymentStep deploymentPlanStep) { - _deploymentPlanService = deploymentPlanService; - _deploymentStepFactories = deploymentStepFactories; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep deploymentStep, DeploymentPlanResult result) + if (!await _deploymentPlanService.DoesUserHavePermissionsAsync()) { - if (deploymentStep is not DeploymentPlanDeploymentStep deploymentPlanStep) - { - return; - } - - if (!await _deploymentPlanService.DoesUserHavePermissionsAsync()) - { - return; - } - - var deploymentStepFactories = _deploymentStepFactories.ToDictionary(f => f.Name); + return; + } - var deploymentPlans = deploymentPlanStep.IncludeAll - ? (await _deploymentPlanService.GetAllDeploymentPlansAsync()).ToArray() - : (await _deploymentPlanService.GetDeploymentPlansAsync(deploymentPlanStep.DeploymentPlanNames)).ToArray(); + var deploymentStepFactories = _deploymentStepFactories.ToDictionary(f => f.Name); - var plans = (from plan in deploymentPlans - select new - { - plan.Name, - Steps = (from step in plan.DeploymentSteps - select new - { - Type = GetStepType(deploymentStepFactories, step), - Step = step - }).ToArray(), - }).ToArray(); + var deploymentPlans = deploymentPlanStep.IncludeAll + ? (await _deploymentPlanService.GetAllDeploymentPlansAsync()).ToArray() + : (await _deploymentPlanService.GetDeploymentPlansAsync(deploymentPlanStep.DeploymentPlanNames)).ToArray(); - // Adding deployment plans. - result.Steps.Add(new JsonObject - { - ["name"] = "deployment", - ["Plans"] = JArray.FromObject(plans, _jsonSerializerOptions), - }); - } + var plans = (from plan in deploymentPlans + select new + { + plan.Name, + Steps = (from step in plan.DeploymentSteps + select new + { + Type = GetStepType(deploymentStepFactories, step), + Step = step + }).ToArray(), + }).ToArray(); - /// - /// A Site Settings Step is generic and the name is mapped to the so its 'Type' should be determined though a lookup. - /// A normal steps name is not mapped to the and should use its type. - /// - private static string GetStepType(Dictionary deploymentStepFactories, DeploymentStep step) + // Adding deployment plans. + result.Steps.Add(new JsonObject { - if (deploymentStepFactories.TryGetValue(step.Name, out var deploymentStepFactory)) - { - return deploymentStepFactory.Name; - } + ["name"] = "deployment", + ["Plans"] = JArray.FromObject(plans, _jsonSerializerOptions), + }); + } - return step.GetType().Name; + /// + /// A Site Settings Step is generic and the name is mapped to the so its 'Type' should be determined though a lookup. + /// A normal steps name is not mapped to the and should use its type. + /// + private static string GetStepType(Dictionary deploymentStepFactories, DeploymentStep step) + { + if (deploymentStepFactories.TryGetValue(step.Name, out var deploymentStepFactory)) + { + return deploymentStepFactory.Name; } + + return step.GetType().Name; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStep.cs index f5bd9b270b4..c7e17e4dc9a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStep.cs @@ -1,17 +1,16 @@ -namespace OrchardCore.Deployment.Deployment +namespace OrchardCore.Deployment.Deployment; + +/// +/// Adds deployment plans to a . +/// +public class DeploymentPlanDeploymentStep : DeploymentStep { - /// - /// Adds deployment plans to a . - /// - public class DeploymentPlanDeploymentStep : DeploymentStep + public DeploymentPlanDeploymentStep() { - public DeploymentPlanDeploymentStep() - { - Name = "DeploymentPlan"; - } + Name = "DeploymentPlan"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] DeploymentPlanNames { get; set; } - } + public string[] DeploymentPlanNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStepDriver.cs index 9b74167f420..2dd0feed769 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Deployment/DeploymentPlanDeploymentStepDriver.cs @@ -4,52 +4,51 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Deployment.Deployment +namespace OrchardCore.Deployment.Deployment; + +public sealed class DeploymentPlanDeploymentStepDriver : DisplayDriver { - public sealed class DeploymentPlanDeploymentStepDriver : DisplayDriver + private readonly IDeploymentPlanService _deploymentPlanService; + + public DeploymentPlanDeploymentStepDriver(IDeploymentPlanService deploymentPlanService) { - private readonly IDeploymentPlanService _deploymentPlanService; + _deploymentPlanService = deploymentPlanService; + } - public DeploymentPlanDeploymentStepDriver(IDeploymentPlanService deploymentPlanService) - { - _deploymentPlanService = deploymentPlanService; - } + public override Task DisplayAsync(DeploymentPlanDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("DeploymentPlanDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("DeploymentPlanDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(DeploymentPlanDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(DeploymentPlanDeploymentStep step, BuildEditorContext context) + { + return Initialize("DeploymentPlanDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("DeploymentPlanDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("DeploymentPlanDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.DeploymentPlanNames = step.DeploymentPlanNames; + model.AllDeploymentPlanNames = (await _deploymentPlanService.GetAllDeploymentPlanNamesAsync()).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(DeploymentPlanDeploymentStep step, BuildEditorContext context) - { - return Initialize("DeploymentPlanDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.DeploymentPlanNames = step.DeploymentPlanNames; - model.AllDeploymentPlanNames = (await _deploymentPlanService.GetAllDeploymentPlanNamesAsync()).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(DeploymentPlanDeploymentStep step, UpdateEditorContext context) + { + step.DeploymentPlanNames = []; + + await context.Updater.TryUpdateModelAsync(step, + Prefix, + x => x.DeploymentPlanNames, + x => x.IncludeAll); - public override async Task UpdateAsync(DeploymentPlanDeploymentStep step, UpdateEditorContext context) + // Don't have the selected option if include all. + if (step.IncludeAll) { step.DeploymentPlanNames = []; - - await context.Updater.TryUpdateModelAsync(step, - Prefix, - x => x.DeploymentPlanNames, - x => x.IncludeAll); - - // Don't have the selected option if include all. - if (step.IncludeAll) - { - step.DeploymentPlanNames = []; - } - - return Edit(step, context); } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/DeploymentPlanService.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/DeploymentPlanService.cs index 625a155fb3f..f3e40e06072 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/DeploymentPlanService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/DeploymentPlanService.cs @@ -8,110 +8,109 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public class DeploymentPlanService : IDeploymentPlanService { - public class DeploymentPlanService : IDeploymentPlanService + private readonly YesSql.ISession _session; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private Dictionary _deploymentPlans; + + public DeploymentPlanService( + YesSql.ISession session, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - private readonly YesSql.ISession _session; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private Dictionary _deploymentPlans; - - public DeploymentPlanService( - YesSql.ISession session, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) - { - _session = session; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - } + _session = session; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - private async Task> GetDeploymentPlans() + private async Task> GetDeploymentPlans() + { + if (_deploymentPlans == null) { - if (_deploymentPlans == null) - { - var deploymentPlanQuery = _session.Query(); - var deploymentPlans = await deploymentPlanQuery.ListAsync(); - _deploymentPlans = deploymentPlans.ToDictionary(x => x.Name); - } - - return _deploymentPlans; + var deploymentPlanQuery = _session.Query(); + var deploymentPlans = await deploymentPlanQuery.ListAsync(); + _deploymentPlans = deploymentPlans.ToDictionary(x => x.Name); } - public async Task DoesUserHavePermissionsAsync() - { - var user = _httpContextAccessor.HttpContext.User; + return _deploymentPlans; + } - var result = await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageDeploymentPlan) && - await _authorizationService.AuthorizeAsync(user, CommonPermissions.Export); + public async Task DoesUserHavePermissionsAsync() + { + var user = _httpContextAccessor.HttpContext.User; - return result; - } + var result = await _authorizationService.AuthorizeAsync(user, CommonPermissions.ManageDeploymentPlan) && + await _authorizationService.AuthorizeAsync(user, CommonPermissions.Export); - public async Task DoesUserHaveExportPermissionAsync() - { - var user = _httpContextAccessor.HttpContext.User; + return result; + } - var result = await _authorizationService.AuthorizeAsync(user, CommonPermissions.Export); + public async Task DoesUserHaveExportPermissionAsync() + { + var user = _httpContextAccessor.HttpContext.User; - return result; - } + var result = await _authorizationService.AuthorizeAsync(user, CommonPermissions.Export); - public async Task> GetAllDeploymentPlanNamesAsync() - { - var deploymentPlans = await GetDeploymentPlans(); + return result; + } - return deploymentPlans.Keys; - } + public async Task> GetAllDeploymentPlanNamesAsync() + { + var deploymentPlans = await GetDeploymentPlans(); - public async Task> GetAllDeploymentPlansAsync() - { - var deploymentPlans = await GetDeploymentPlans(); + return deploymentPlans.Keys; + } - return deploymentPlans.Values; - } + public async Task> GetAllDeploymentPlansAsync() + { + var deploymentPlans = await GetDeploymentPlans(); - public async Task> GetDeploymentPlansAsync(params string[] deploymentPlanNames) - { - var deploymentPlans = await GetDeploymentPlans(); + return deploymentPlans.Values; + } - return GetDeploymentPlans(deploymentPlans, deploymentPlanNames); - } + public async Task> GetDeploymentPlansAsync(params string[] deploymentPlanNames) + { + var deploymentPlans = await GetDeploymentPlans(); - private static IEnumerable GetDeploymentPlans(Dictionary deploymentPlans, params string[] deploymentPlanNames) + return GetDeploymentPlans(deploymentPlans, deploymentPlanNames); + } + + private static IEnumerable GetDeploymentPlans(Dictionary deploymentPlans, params string[] deploymentPlanNames) + { + foreach (var deploymentPlanName in deploymentPlanNames) { - foreach (var deploymentPlanName in deploymentPlanNames) + if (deploymentPlans.TryGetValue(deploymentPlanName, out var deploymentPlan)) { - if (deploymentPlans.TryGetValue(deploymentPlanName, out var deploymentPlan)) - { - yield return deploymentPlan; - } + yield return deploymentPlan; } } + } - public async Task CreateOrUpdateDeploymentPlansAsync(IEnumerable deploymentPlans) - { - var names = deploymentPlans.Select(x => x.Name); + public async Task CreateOrUpdateDeploymentPlansAsync(IEnumerable deploymentPlans) + { + var names = deploymentPlans.Select(x => x.Name); - var existingDeploymentPlans = (await _session.Query(x => x.Name.IsIn(names)) - .ListAsync()) - .ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + var existingDeploymentPlans = (await _session.Query(x => x.Name.IsIn(names)) + .ListAsync()) + .ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); - foreach (var deploymentPlan in deploymentPlans) + foreach (var deploymentPlan in deploymentPlans) + { + if (existingDeploymentPlans.TryGetValue(deploymentPlan.Name, out var existingDeploymentPlan)) + { + existingDeploymentPlan.Name = deploymentPlan.Name; + existingDeploymentPlan.DeploymentSteps.Clear(); + existingDeploymentPlan.DeploymentSteps.AddRange(deploymentPlan.DeploymentSteps); + + await _session.SaveAsync(existingDeploymentPlan); + } + else { - if (existingDeploymentPlans.TryGetValue(deploymentPlan.Name, out var existingDeploymentPlan)) - { - existingDeploymentPlan.Name = deploymentPlan.Name; - existingDeploymentPlan.DeploymentSteps.Clear(); - existingDeploymentPlan.DeploymentSteps.AddRange(deploymentPlan.DeploymentSteps); - - await _session.SaveAsync(existingDeploymentPlan); - } - else - { - await _session.SaveAsync(deploymentPlan); - } + await _session.SaveAsync(deploymentPlan); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Indexes/DeploymentPlanIndex.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Indexes/DeploymentPlanIndex.cs index a9b1834981e..de11b7d6b27 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Indexes/DeploymentPlanIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Indexes/DeploymentPlanIndex.cs @@ -1,26 +1,25 @@ using YesSql.Indexes; -namespace OrchardCore.Deployment.Indexes +namespace OrchardCore.Deployment.Indexes; + +public class DeploymentPlanIndex : MapIndex { - public class DeploymentPlanIndex : MapIndex - { - public long DocumentId { get; set; } + public long DocumentId { get; set; } - public string Name { get; set; } - } + public string Name { get; set; } +} - public class DeploymentPlanIndexProvider : IndexProvider +public class DeploymentPlanIndexProvider : IndexProvider +{ + public override void Describe(DescribeContext context) { - public override void Describe(DescribeContext context) - { - context.For() - .Map(deploymentPlan => + context.For() + .Map(deploymentPlan => + { + return new DeploymentPlanIndex { - return new DeploymentPlanIndex - { - Name = deploymentPlan.Name - }; - }); - } + Name = deploymentPlan.Name + }; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Migrations.cs index 2d0221b6ceb..c0162a87c7c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Migrations.cs @@ -3,17 +3,16 @@ using OrchardCore.Deployment.Indexes; using YesSql.Sql; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + public async Task CreateAsync() { - public async Task CreateAsync() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("Name") - ); + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("Name") + ); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Recipes/DeploymentPlansRecipeStep.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Recipes/DeploymentPlansRecipeStep.cs index 1b2551ad69d..e17a064b637 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Recipes/DeploymentPlansRecipeStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Recipes/DeploymentPlansRecipeStep.cs @@ -11,98 +11,97 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Deployment.Recipes +namespace OrchardCore.Deployment.Recipes; + +/// +/// This recipe step creates a deployment plan. +/// +public sealed class DeploymentPlansRecipeStep : IRecipeStepHandler { - /// - /// This recipe step creates a deployment plan. - /// - public sealed class DeploymentPlansRecipeStep : IRecipeStepHandler - { - private readonly IServiceProvider _serviceProvider; - private readonly JsonSerializerOptions _jsonSerializerOptions; - private readonly IDeploymentPlanService _deploymentPlanService; + private readonly IServiceProvider _serviceProvider; + private readonly JsonSerializerOptions _jsonSerializerOptions; + private readonly IDeploymentPlanService _deploymentPlanService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; + + public DeploymentPlansRecipeStep( + IServiceProvider serviceProvider, + IOptions jsonSerializerOptions, + IDeploymentPlanService deploymentPlanService, + IStringLocalizer stringLocalizer) + { + _serviceProvider = serviceProvider; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + _deploymentPlanService = deploymentPlanService; + S = stringLocalizer; + } - public DeploymentPlansRecipeStep( - IServiceProvider serviceProvider, - IOptions jsonSerializerOptions, - IDeploymentPlanService deploymentPlanService, - IStringLocalizer stringLocalizer) + public Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "deployment", StringComparison.OrdinalIgnoreCase)) { - _serviceProvider = serviceProvider; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; - _deploymentPlanService = deploymentPlanService; - S = stringLocalizer; + return Task.CompletedTask; } - public Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "deployment", StringComparison.OrdinalIgnoreCase)) - { - return Task.CompletedTask; - } + var deploymentStepFactories = _serviceProvider.GetServices().ToDictionary(f => f.Name); - var deploymentStepFactories = _serviceProvider.GetServices().ToDictionary(f => f.Name); + var model = context.Step.ToObject(); - var model = context.Step.ToObject(); + var unknownTypes = new List(); + var deploymentPlans = new List(); - var unknownTypes = new List(); - var deploymentPlans = new List(); + foreach (var plan in model.Plans) + { + var deploymentPlan = new DeploymentPlan + { + Name = plan.Name + }; - foreach (var plan in model.Plans) + foreach (var step in plan.Steps) { - var deploymentPlan = new DeploymentPlan + if (deploymentStepFactories.TryGetValue(step.Type, out var deploymentStepFactory)) { - Name = plan.Name - }; + var deploymentStep = (DeploymentStep)step.Step.ToObject(deploymentStepFactory.Create().GetType(), _jsonSerializerOptions); - foreach (var step in plan.Steps) + deploymentPlan.DeploymentSteps.Add(deploymentStep); + } + else { - if (deploymentStepFactories.TryGetValue(step.Type, out var deploymentStepFactory)) - { - var deploymentStep = (DeploymentStep)step.Step.ToObject(deploymentStepFactory.Create().GetType(), _jsonSerializerOptions); - - deploymentPlan.DeploymentSteps.Add(deploymentStep); - } - else - { - unknownTypes.Add(step.Type); - } + unknownTypes.Add(step.Type); } - - deploymentPlans.Add(deploymentPlan); - } - - if (unknownTypes.Count != 0) - { - context.Errors.Add( - S["No changes have been made. The following types of deployment plans cannot be added: {0}. Please ensure that the related features are enabled to add these types of deployment plans.", - string.Join(", ", unknownTypes)]); - - return Task.CompletedTask; } - return _deploymentPlanService.CreateOrUpdateDeploymentPlansAsync(deploymentPlans); + deploymentPlans.Add(deploymentPlan); } - private sealed class DeploymentPlansModel + if (unknownTypes.Count != 0) { - public DeploymentPlanModel[] Plans { get; set; } + context.Errors.Add( + S["No changes have been made. The following types of deployment plans cannot be added: {0}. Please ensure that the related features are enabled to add these types of deployment plans.", + string.Join(", ", unknownTypes)]); + + return Task.CompletedTask; } - private sealed class DeploymentPlanModel - { - public string Name { get; set; } + return _deploymentPlanService.CreateOrUpdateDeploymentPlansAsync(deploymentPlans); + } - public DeploymentStepModel[] Steps { get; set; } - } + private sealed class DeploymentPlansModel + { + public DeploymentPlanModel[] Plans { get; set; } + } - private sealed class DeploymentStepModel - { - public string Type { get; set; } + private sealed class DeploymentPlanModel + { + public string Name { get; set; } - public JsonObject Step { get; set; } - } + public DeploymentStepModel[] Steps { get; set; } + } + + private sealed class DeploymentStepModel + { + public string Type { get; set; } + + public JsonObject Step { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Services/FileDownloadDeploymentTargetProvider.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Services/FileDownloadDeploymentTargetProvider.cs index 575053e9dfa..841c121dea1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Services/FileDownloadDeploymentTargetProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Services/FileDownloadDeploymentTargetProvider.cs @@ -3,33 +3,32 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Localization; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public class FileDownloadDeploymentTargetProvider : IDeploymentTargetProvider { - public class FileDownloadDeploymentTargetProvider : IDeploymentTargetProvider - { - protected readonly IStringLocalizer S; + protected readonly IStringLocalizer S; - public FileDownloadDeploymentTargetProvider(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public FileDownloadDeploymentTargetProvider(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public Task> GetDeploymentTargetsAsync() - { - return Task.FromResult>( - new[] { - new DeploymentTarget( - name: S["File Download"], - description: S["Download a deployment plan locally."], - route: new RouteValueDictionary(new - { - area = "OrchardCore.Deployment", - controller = "ExportFile", - action = "Execute" - }) - ) - } - ); - } + public Task> GetDeploymentTargetsAsync() + { + return Task.FromResult>( + new[] { + new DeploymentTarget( + name: S["File Download"], + description: S["Download a deployment plan locally."], + route: new RouteValueDictionary(new + { + area = "OrchardCore.Deployment", + controller = "ExportFile", + action = "Execute" + }) + ) + } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Startup.cs index b774ddf54e2..41ea146766e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Startup.cs @@ -11,35 +11,34 @@ using OrchardCore.Recipes; using OrchardCore.Security.Permissions; -namespace OrchardCore.Deployment +namespace OrchardCore.Deployment; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeploymentServices(); + services.AddDeploymentServices(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddSingleton(); + services.AddSingleton(); - // Custom File deployment step - services.AddDeployment(); + // Custom File deployment step + services.AddDeployment(); - // Recipe File deployment step - services.AddDeploymentWithoutSource(); + // Recipe File deployment step + services.AddDeploymentWithoutSource(); - services.AddIndexProvider(); - services.AddDataMigration(); + services.AddIndexProvider(); + services.AddDataMigration(); - services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.AddDeployment(); + services.AddDeployment(); - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentSource.cs index c5586213017..92c97b2fba8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentSource.cs @@ -1,18 +1,17 @@ using System.Text; using System.Threading.Tasks; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +public class CustomFileDeploymentSource : IDeploymentSource { - public class CustomFileDeploymentSource : IDeploymentSource + public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (step is not CustomFileDeploymentStep customFile) { - if (step is not CustomFileDeploymentStep customFile) - { - return Task.CompletedTask; - } - - return result.FileBuilder.SetFileAsync(customFile.FileName, Encoding.UTF8.GetBytes(customFile.FileContent)); + return Task.CompletedTask; } + + return result.FileBuilder.SetFileAsync(customFile.FileName, Encoding.UTF8.GetBytes(customFile.FileContent)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStep.cs index 1e46838f67c..f283191ef5d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStep.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +/// +/// Adds a custom file to a . +/// +public class CustomFileDeploymentStep : DeploymentStep { - /// - /// Adds a custom file to a . - /// - public class CustomFileDeploymentStep : DeploymentStep + public CustomFileDeploymentStep() { - public CustomFileDeploymentStep() - { - Name = nameof(CustomFileDeploymentStep); - } + Name = nameof(CustomFileDeploymentStep); + } - [Required] - public string FileName { get; set; } + [Required] + public string FileName { get; set; } - public string FileContent { get; set; } - } + public string FileContent { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStepDriver.cs index a9c4dee72df..cb8b97f7bb4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/CustomFileDeploymentStepDriver.cs @@ -3,33 +3,32 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +public sealed class CustomFileDeploymentStepDriver : DisplayDriver { - public sealed class CustomFileDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(CustomFileDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(CustomFileDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("CustomFileDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("CustomFileDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("CustomFileDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("CustomFileDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(CustomFileDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(CustomFileDeploymentStep step, BuildEditorContext context) + { + return Initialize("CustomFileDeploymentStep_Fields_Edit", model => { - return Initialize("CustomFileDeploymentStep_Fields_Edit", model => - { - model.FileContent = step.FileContent; - model.FileName = step.FileName; - }).Location("Content"); - } + model.FileContent = step.FileContent; + model.FileName = step.FileName; + }).Location("Content"); + } - public override async Task UpdateAsync(CustomFileDeploymentStep step, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.FileName, x => x.FileContent); + public override async Task UpdateAsync(CustomFileDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.FileName, x => x.FileContent); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentSource.cs index 246f94b28bc..66c01479197 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentSource.cs @@ -1,20 +1,19 @@ using System.Text.Json.Nodes; using System.Threading.Tasks; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +public class JsonRecipeDeploymentSource : IDeploymentSource { - public class JsonRecipeDeploymentSource : IDeploymentSource + public Task ProcessDeploymentStepAsync(DeploymentStep deploymentStep, DeploymentPlanResult result) { - public Task ProcessDeploymentStepAsync(DeploymentStep deploymentStep, DeploymentPlanResult result) + if (deploymentStep is not JsonRecipeDeploymentStep jsonRecipeStep) { - if (deploymentStep is not JsonRecipeDeploymentStep jsonRecipeStep) - { - return Task.CompletedTask; - } - - result.Steps.Add(JObject.Parse(jsonRecipeStep.Json)); - return Task.CompletedTask; } + + result.Steps.Add(JObject.Parse(jsonRecipeStep.Json)); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStep.cs index bd3cb001358..d8c42b8c98b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStep.cs @@ -1,15 +1,14 @@ -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +/// +/// Adds a JSON recipe to a . +/// +public class JsonRecipeDeploymentStep : DeploymentStep { - /// - /// Adds a JSON recipe to a . - /// - public class JsonRecipeDeploymentStep : DeploymentStep + public JsonRecipeDeploymentStep() { - public JsonRecipeDeploymentStep() - { - Name = "JsonRecipe"; - } - - public string Json { get; set; } + Name = "JsonRecipe"; } + + public string Json { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStepDriver.cs index 6e2b98d4ec4..42fc2c854ed 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/JsonRecipeDeploymentStepDriver.cs @@ -7,14 +7,14 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +public sealed class JsonRecipeDeploymentStepDriver : DisplayDriver { - public sealed class JsonRecipeDeploymentStepDriver : DisplayDriver - { - /// - /// A limited schema for recipe steps. Does not include any step data. - /// - public const string Schema = @" + /// + /// A limited schema for recipe steps. Does not include any step data. + /// + public const string Schema = @" { ""$schema"": ""http://json-schema.org/draft-04/schema#"", ""type"": ""object"", @@ -30,56 +30,55 @@ public sealed class JsonRecipeDeploymentStepDriver : DisplayDriver stringLocalizer) - { - S = stringLocalizer; - } - - public override Task DisplayAsync(JsonRecipeDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("JsonRecipeDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("JsonRecipeDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + public JsonRecipeDeploymentStepDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public override IDisplayResult Edit(JsonRecipeDeploymentStep step, BuildEditorContext context) - { - return Initialize("JsonRecipeDeploymentStep_Fields_Edit", model => - { - model.Json = step.Json; - model.Schema = Schema; - }).Location("Content"); - } + public override Task DisplayAsync(JsonRecipeDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("JsonRecipeDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("JsonRecipeDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override async Task UpdateAsync(JsonRecipeDeploymentStep step, UpdateEditorContext context) + public override IDisplayResult Edit(JsonRecipeDeploymentStep step, BuildEditorContext context) + { + return Initialize("JsonRecipeDeploymentStep_Fields_Edit", model => { - var model = new JsonRecipeDeploymentStepViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); + model.Json = step.Json; + model.Schema = Schema; + }).Location("Content"); + } - try - { - var jObject = JObject.Parse(model.Json); - if (!jObject.ContainsKey("name")) - { + public override async Task UpdateAsync(JsonRecipeDeploymentStep step, UpdateEditorContext context) + { + var model = new JsonRecipeDeploymentStepViewModel(); - context.Updater.ModelState.AddModelError(Prefix, nameof(JsonRecipeDeploymentStepViewModel.Json), S["The recipe must have a name property"]); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - } - catch (Exception) + try + { + var jObject = JObject.Parse(model.Json); + if (!jObject.ContainsKey("name")) { - context.Updater.ModelState.AddModelError(Prefix, nameof(JsonRecipeDeploymentStepViewModel.Json), S["Invalid JSON supplied"]); + context.Updater.ModelState.AddModelError(Prefix, nameof(JsonRecipeDeploymentStepViewModel.Json), S["The recipe must have a name property"]); } - step.Json = model.Json; + } + catch (Exception) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(JsonRecipeDeploymentStepViewModel.Json), S["Invalid JSON supplied"]); - return Edit(step, context); } + + step.Json = model.Json; + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStep.cs index e63f2bf2514..136ab6e1757 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStep.cs @@ -1,31 +1,30 @@ -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +/// +/// Adds a Recipe file to a . +/// +public class RecipeFileDeploymentStep : DeploymentStep { - /// - /// Adds a Recipe file to a . - /// - public class RecipeFileDeploymentStep : DeploymentStep + public RecipeFileDeploymentStep() { - public RecipeFileDeploymentStep() - { - Name = nameof(RecipeFileDeploymentStep); - } + Name = nameof(RecipeFileDeploymentStep); + } - public string RecipeName { get; set; } + public string RecipeName { get; set; } - public string DisplayName { get; set; } + public string DisplayName { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public string Author { get; set; } + public string Author { get; set; } - public string WebSite { get; set; } + public string WebSite { get; set; } - public string Version { get; set; } + public string Version { get; set; } - public bool IsSetupRecipe { get; set; } + public bool IsSetupRecipe { get; set; } - public string Categories { get; set; } + public string Categories { get; set; } - public string Tags { get; set; } - } + public string Tags { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStepDriver.cs index 5d526cd0808..12a1e0ddb70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/Steps/RecipeFileDeploymentStepDriver.cs @@ -3,49 +3,48 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Deployment.Steps +namespace OrchardCore.Deployment.Steps; + +public sealed class RecipeFileDeploymentStepDriver : DisplayDriver { - public sealed class RecipeFileDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(RecipeFileDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(RecipeFileDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("RecipeFileDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("RecipeFileDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("RecipeFileDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("RecipeFileDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(RecipeFileDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(RecipeFileDeploymentStep step, BuildEditorContext context) + { + return Initialize("RecipeFileDeploymentStep_Fields_Edit", model => { - return Initialize("RecipeFileDeploymentStep_Fields_Edit", model => - { - model.RecipeName = step.RecipeName; - model.DisplayName = step.DisplayName; - model.Description = step.Description; - model.Author = step.Author; - model.WebSite = step.WebSite; - model.Version = step.Version; - model.IsSetupRecipe = step.IsSetupRecipe; - model.Categories = step.Categories; - model.Tags = step.Tags; - }).Location("Content"); - } + model.RecipeName = step.RecipeName; + model.DisplayName = step.DisplayName; + model.Description = step.Description; + model.Author = step.Author; + model.WebSite = step.WebSite; + model.Version = step.Version; + model.IsSetupRecipe = step.IsSetupRecipe; + model.Categories = step.Categories; + model.Tags = step.Tags; + }).Location("Content"); + } - public override async Task UpdateAsync(RecipeFileDeploymentStep step, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(step, - Prefix, x => x.RecipeName, - x => x.DisplayName, - x => x.Description, - x => x.Author, - x => x.WebSite, - x => x.Version, - x => x.IsSetupRecipe, - x => x.Categories, - x => x.Tags); + public override async Task UpdateAsync(RecipeFileDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, + Prefix, x => x.RecipeName, + x => x.DisplayName, + x => x.Description, + x => x.Author, + x => x.WebSite, + x => x.Version, + x => x.IsSetupRecipe, + x => x.Categories, + x => x.Tags); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CreateDeploymentPlanViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CreateDeploymentPlanViewModel.cs index af6a66d2f2d..ed4cb274bd3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CreateDeploymentPlanViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CreateDeploymentPlanViewModel.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class CreateDeploymentPlanViewModel { - public class CreateDeploymentPlanViewModel - { - [Required] - public string Name { get; set; } - } + [Required] + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CustomFileDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CustomFileDeploymentStepViewModel.cs index 5e9de0b629a..f7e45eebdab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CustomFileDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/CustomFileDeploymentStepViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class CustomFileDeploymentStepViewModel { - public class CustomFileDeploymentStepViewModel - { - public string FileName { get; set; } - public string FileContent { get; set; } - } + public string FileName { get; set; } + public string FileContent { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanDeploymentStepViewModel.cs index a30aa7e42f7..d5138879193 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class DeploymentPlanDeploymentStepViewModel { - public class DeploymentPlanDeploymentStepViewModel - { - public bool IncludeAll { get; set; } = true; - public string[] DeploymentPlanNames { get; set; } - public string[] AllDeploymentPlanNames { get; set; } - } + public bool IncludeAll { get; set; } = true; + public string[] DeploymentPlanNames { get; set; } + public string[] AllDeploymentPlanNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanIndexViewModel.cs index aa9f0428fea..587f7cc8ef1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DeploymentPlanIndexViewModel.cs @@ -2,33 +2,32 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class DeploymentPlanIndexViewModel { - public class DeploymentPlanIndexViewModel - { - public IList DeploymentPlans { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - public dynamic Pager { get; set; } - } + public IList DeploymentPlans { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + public dynamic Pager { get; set; } +} - public class DeploymentPlanEntry - { - public DeploymentPlan DeploymentPlan { get; set; } - public bool IsChecked { get; set; } - } +public class DeploymentPlanEntry +{ + public DeploymentPlan DeploymentPlan { get; set; } + public bool IsChecked { get; set; } +} - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } - [BindNever] - public List DeploymentPlansBulkAction { get; set; } - } + [BindNever] + public List DeploymentPlansBulkAction { get; set; } +} - public enum ContentsBulkAction - { - None, - Delete - } +public enum ContentsBulkAction +{ + None, + Delete } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DisplayDeploymentPlanViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DisplayDeploymentPlanViewModel.cs index 059d797003e..97ec6e87d71 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DisplayDeploymentPlanViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/DisplayDeploymentPlanViewModel.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class DisplayDeploymentPlanViewModel { - public class DisplayDeploymentPlanViewModel - { - public DeploymentPlan DeploymentPlan { get; set; } - public IEnumerable Items { get; set; } - public IDictionary Thumbnails { get; set; } - } + public DeploymentPlan DeploymentPlan { get; set; } + public IEnumerable Items { get; set; } + public IDictionary Thumbnails { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanStepViewModel.cs index e33703600ca..31820fd7f85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanStepViewModel.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class EditDeploymentPlanStepViewModel { - public class EditDeploymentPlanStepViewModel - { - public long DeploymentPlanId { get; set; } - public string DeploymentStepId { get; set; } - public string DeploymentStepType { get; set; } - public dynamic Editor { get; set; } + public long DeploymentPlanId { get; set; } + public string DeploymentStepId { get; set; } + public string DeploymentStepType { get; set; } + public dynamic Editor { get; set; } - [BindNever] - public DeploymentStep DeploymentStep { get; set; } - } + [BindNever] + public DeploymentStep DeploymentStep { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanViewModel.cs index a8c9e5a76b1..a6c56abdf99 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/EditDeploymentPlanViewModel.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class EditDeploymentPlanViewModel { - public class EditDeploymentPlanViewModel - { - public long Id { get; set; } + public long Id { get; set; } - [Required] - public string Name { get; set; } - } + [Required] + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/ImportJsonViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/ImportJsonViewModel.cs index 1de493af169..d7ebbc5b6ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/ImportJsonViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/ImportJsonViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class ImportJsonViewModel { - public class ImportJsonViewModel - { - public string Json { get; set; } - } + public string Json { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/JsonRecipeDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/JsonRecipeDeploymentStepViewModel.cs index b7b9509ca8d..e934fb36ab6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/JsonRecipeDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/JsonRecipeDeploymentStepViewModel.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class JsonRecipeDeploymentStepViewModel { - public class JsonRecipeDeploymentStepViewModel - { - public string Json { get; set; } + public string Json { get; set; } - [BindNever] - public string Schema { get; set; } - } + [BindNever] + public string Schema { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/RecipeFileDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/RecipeFileDeploymentStepViewModel.cs index 94abc0a7798..33b27f23ebf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/RecipeFileDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Deployment/ViewModels/RecipeFileDeploymentStepViewModel.cs @@ -1,15 +1,14 @@ -namespace OrchardCore.Deployment.ViewModels +namespace OrchardCore.Deployment.ViewModels; + +public class RecipeFileDeploymentStepViewModel { - public class RecipeFileDeploymentStepViewModel - { - public string RecipeName { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public string Author { get; set; } - public string WebSite { get; set; } - public string Version { get; set; } - public bool IsSetupRecipe { get; set; } - public string Categories { get; set; } - public string Tags { get; set; } - } + public string RecipeName { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string Author { get; set; } + public string WebSite { get; set; } + public string Version { get; set; } + public bool IsSetupRecipe { get; set; } + public string Categories { get; set; } + public string Tags { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Diagnostics/DiagnosticsStartupFilter.cs b/src/OrchardCore.Modules/OrchardCore.Diagnostics/DiagnosticsStartupFilter.cs index ba35a1d1d27..dffeca50fdf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Diagnostics/DiagnosticsStartupFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Diagnostics/DiagnosticsStartupFilter.cs @@ -5,49 +5,48 @@ using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Hosting; -namespace OrchardCore.Diagnostics +namespace OrchardCore.Diagnostics; + +public sealed class DiagnosticsStartupFilter : IStartupFilter { - public sealed class DiagnosticsStartupFilter : IStartupFilter - { - private readonly FileExtensionContentTypeProvider _contentTypeProvider = new(); + private readonly FileExtensionContentTypeProvider _contentTypeProvider = new(); - private readonly IHostEnvironment _hostEnvironment; + private readonly IHostEnvironment _hostEnvironment; - public DiagnosticsStartupFilter(IHostEnvironment hostEnvironment) - { - _hostEnvironment = hostEnvironment; - } + public DiagnosticsStartupFilter(IHostEnvironment hostEnvironment) + { + _hostEnvironment = hostEnvironment; + } - public Action Configure(Action next) + public Action Configure(Action next) + { + return app => { - return app => + if (!_hostEnvironment.IsDevelopment()) { - if (!_hostEnvironment.IsDevelopment()) - { - app.UseExceptionHandler("/Error"); - } + app.UseExceptionHandler("/Error"); + } - app.UseStatusCodePagesWithReExecute("/Error/{0}"); + app.UseStatusCodePagesWithReExecute("/Error/{0}"); - app.Use(async (context, next) => - { - await next(); + app.Use(async (context, next) => + { + await next(); - if (context.Response.StatusCode < 200 || context.Response.StatusCode >= 400) + if (context.Response.StatusCode < 200 || context.Response.StatusCode >= 400) + { + if (_contentTypeProvider.TryGetContentType(context.Request.Path.Value, out var contentType)) { - if (_contentTypeProvider.TryGetContentType(context.Request.Path.Value, out var contentType)) + var statusCodePagesFeature = context.Features.Get(); + if (statusCodePagesFeature != null) { - var statusCodePagesFeature = context.Features.Get(); - if (statusCodePagesFeature != null) - { - statusCodePagesFeature.Enabled = false; - } + statusCodePagesFeature.Enabled = false; } } - }); + } + }); - next(app); - }; - } + next(app); + }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Diagnostics/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Diagnostics/Startup.cs index 80b2c3d53cb..7c396e9570b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Diagnostics/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Diagnostics/Startup.cs @@ -5,23 +5,22 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -namespace OrchardCore.Diagnostics +namespace OrchardCore.Diagnostics; + +public sealed class Startup : Modules.StartupBase { - public sealed class Startup : Modules.StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.TryAddEnumerable(ServiceDescriptor.Singleton()); - } + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "Diagnostics.Error", - areaName: "OrchardCore.Diagnostics", - pattern: "Error/{status?}", - defaults: new { controller = "Diagnostics", action = "Error" } - ); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "Diagnostics.Error", + areaName: "OrchardCore.Diagnostics", + pattern: "Error/{status?}", + defaults: new { controller = "Diagnostics", action = "Error" } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheContextEntryExtensions.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheContextEntryExtensions.cs index 6b8fad72b62..5ca186fd991 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheContextEntryExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheContextEntryExtensions.cs @@ -3,20 +3,19 @@ using System.Text; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache +namespace OrchardCore.DynamicCache; + +public static class CacheContextEntryExtensions { - public static class CacheContextEntryExtensions + public static string GetContextHash(this IEnumerable entries) { - public static string GetContextHash(this IEnumerable entries) + var sb = new StringBuilder(); + foreach (var entry in entries.OrderBy(x => x.Key).ThenBy(x => x.Value)) { - var sb = new StringBuilder(); - foreach (var entry in entries.OrderBy(x => x.Key).ThenBy(x => x.Value)) - { - var part = entry.Key + entry.Value; - sb.Append(part); - } - - return sb.ToString(); + var part = entry.Key + entry.Value; + sb.Append(part); } + + return sb.ToString(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheOptionsConfiguration.cs index c9877e15ced..20a70e696a8 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CacheOptionsConfiguration.cs @@ -3,42 +3,41 @@ using OrchardCore.Environment.Cache; using OrchardCore.Settings; -namespace OrchardCore.DynamicCache +namespace OrchardCore.DynamicCache; + +public sealed class CacheOptionsConfiguration : IConfigureOptions { - public sealed class CacheOptionsConfiguration : IConfigureOptions + private readonly ISiteService _siteService; + private readonly IHostEnvironment _env; + + public CacheOptionsConfiguration(ISiteService siteService, IHostEnvironment env) { - private readonly ISiteService _siteService; - private readonly IHostEnvironment _env; + _siteService = siteService; + _env = env; + } - public CacheOptionsConfiguration(ISiteService siteService, IHostEnvironment env) - { - _siteService = siteService; - _env = env; - } + public void Configure(CacheOptions options) + { + var settings = _siteService.GetSiteSettingsAsync().GetAwaiter().GetResult(); - public void Configure(CacheOptions options) + switch (settings.CacheMode) { - var settings = _siteService.GetSiteSettingsAsync().GetAwaiter().GetResult(); - - switch (settings.CacheMode) - { - case CacheMode.Enabled: - options.Enabled = true; - break; - - case CacheMode.DebugEnabled: - options.Enabled = true; - options.DebugMode = true; - break; - - case CacheMode.Disabled: - options.Enabled = false; - break; - - case CacheMode.FromConfiguration: - options.Enabled = _env.IsProduction(); - break; - } + case CacheMode.Enabled: + options.Enabled = true; + break; + + case CacheMode.DebugEnabled: + options.Enabled = true; + options.DebugMode = true; + break; + + case CacheMode.Disabled: + options.Enabled = false; + break; + + case CacheMode.FromConfiguration: + options.Enabled = _env.IsProduction(); + break; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CachedShapeWrapperShapes.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CachedShapeWrapperShapes.cs index 7e39fbbf843..b00f445341d 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/CachedShapeWrapperShapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/CachedShapeWrapperShapes.cs @@ -4,36 +4,35 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.DynamicCache +namespace OrchardCore.DynamicCache; + +public class CachedShapeWrapperShapes : IShapeAttributeProvider { - public class CachedShapeWrapperShapes : IShapeAttributeProvider - { - [Shape] + [Shape] #pragma warning disable CA1822 // Mark members as static - public IHtmlContent CachedShapeWrapper(IShape Shape) + public IHtmlContent CachedShapeWrapper(IShape Shape) #pragma warning restore CA1822 // Mark members as static - { - // No need to optimize this code as it will be used for debugging purpose + { + // No need to optimize this code as it will be used for debugging purpose - var contentBuilder = new HtmlContentBuilder(); - var metadata = Shape.Metadata; - var cache = metadata.Cache(); + var contentBuilder = new HtmlContentBuilder(); + var metadata = Shape.Metadata; + var cache = metadata.Cache(); - contentBuilder.AppendLine(); - contentBuilder.AppendHtmlLine($""); + contentBuilder.AppendLine(); + contentBuilder.AppendHtmlLine($""); - contentBuilder.AppendHtml(metadata.ChildContent); + contentBuilder.AppendHtml(metadata.ChildContent); - contentBuilder.AppendLine(); - contentBuilder.AppendHtmlLine($""); + contentBuilder.AppendLine(); + contentBuilder.AppendHtmlLine($""); - return contentBuilder; - } + return contentBuilder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/EventHandlers/DynamicCacheShapeDisplayEvents.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/EventHandlers/DynamicCacheShapeDisplayEvents.cs index 231640fa7ec..82ce6efd772 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/EventHandlers/DynamicCacheShapeDisplayEvents.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/EventHandlers/DynamicCacheShapeDisplayEvents.cs @@ -7,119 +7,118 @@ using OrchardCore.DisplayManagement.Implementation; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache.EventHandlers +namespace OrchardCore.DynamicCache.EventHandlers; + +/// +/// Caches shapes in the default implementation. +/// It uses the shape's metadata cache context to define the cache parameters. +/// +public class DynamicCacheShapeDisplayEvents : IShapeDisplayEvents { - /// - /// Caches shapes in the default implementation. - /// It uses the shape's metadata cache context to define the cache parameters. - /// - public class DynamicCacheShapeDisplayEvents : IShapeDisplayEvents + private readonly Dictionary _cached = []; + private readonly Dictionary _openScopes = []; + + private readonly IDynamicCacheService _dynamicCacheService; + private readonly ICacheScopeManager _cacheScopeManager; + private readonly HtmlEncoder _htmlEncoder; + private readonly CacheOptions _cacheOptions; + + public DynamicCacheShapeDisplayEvents( + IDynamicCacheService dynamicCacheService, + ICacheScopeManager cacheScopeManager, + HtmlEncoder htmlEncoder, + IOptions options) { - private readonly Dictionary _cached = []; - private readonly Dictionary _openScopes = []; - - private readonly IDynamicCacheService _dynamicCacheService; - private readonly ICacheScopeManager _cacheScopeManager; - private readonly HtmlEncoder _htmlEncoder; - private readonly CacheOptions _cacheOptions; - - public DynamicCacheShapeDisplayEvents( - IDynamicCacheService dynamicCacheService, - ICacheScopeManager cacheScopeManager, - HtmlEncoder htmlEncoder, - IOptions options) + _dynamicCacheService = dynamicCacheService; + _cacheScopeManager = cacheScopeManager; + _htmlEncoder = htmlEncoder; + _cacheOptions = options.Value; + } + + public async Task DisplayingAsync(ShapeDisplayContext context) + { + if (!_cacheOptions.Enabled) { - _dynamicCacheService = dynamicCacheService; - _cacheScopeManager = cacheScopeManager; - _htmlEncoder = htmlEncoder; - _cacheOptions = options.Value; + return; } - public async Task DisplayingAsync(ShapeDisplayContext context) + // The shape has cache settings and no content yet. + if (context.Shape.Metadata.IsCached && context.ChildContent == null) { - if (!_cacheOptions.Enabled) + var cacheContext = context.Shape.Metadata.Cache(); + _cacheScopeManager.EnterScope(cacheContext); + _openScopes[cacheContext.CacheId] = cacheContext; + + var cachedContent = await _dynamicCacheService.GetCachedValueAsync(cacheContext); + + if (cachedContent != null) { - return; + // The contents of this shape was found in the cache. + // Add the cacheContext to _cached so that we don't try to cache the content again in the DisplayedAsync method. + _cached[cacheContext.CacheId] = cacheContext; + context.ChildContent = new HtmlString(cachedContent); } - - // The shape has cache settings and no content yet. - if (context.Shape.Metadata.IsCached && context.ChildContent == null) + else if (_cacheOptions.DebugMode) { - var cacheContext = context.Shape.Metadata.Cache(); - _cacheScopeManager.EnterScope(cacheContext); - _openScopes[cacheContext.CacheId] = cacheContext; - - var cachedContent = await _dynamicCacheService.GetCachedValueAsync(cacheContext); - - if (cachedContent != null) - { - // The contents of this shape was found in the cache. - // Add the cacheContext to _cached so that we don't try to cache the content again in the DisplayedAsync method. - _cached[cacheContext.CacheId] = cacheContext; - context.ChildContent = new HtmlString(cachedContent); - } - else if (_cacheOptions.DebugMode) - { - context.Shape.Metadata.Wrappers.Add("CachedShapeWrapper"); - } + context.Shape.Metadata.Wrappers.Add("CachedShapeWrapper"); } } + } - public async Task DisplayedAsync(ShapeDisplayContext context) + public async Task DisplayedAsync(ShapeDisplayContext context) + { + if (!_cacheOptions.Enabled) { - if (!_cacheOptions.Enabled) - { - return; - } + return; + } - var cacheContext = context.Shape.Metadata.Cache(); + var cacheContext = context.Shape.Metadata.Cache(); - // If the shape is not configured to be cached, continue as usual. - if (cacheContext == null) - { - context.ChildContent ??= HtmlString.Empty; + // If the shape is not configured to be cached, continue as usual. + if (cacheContext == null) + { + context.ChildContent ??= HtmlString.Empty; - return; - } + return; + } - // If we have got this far, then this shape is configured to be cached. - // We need to determine whether or not the ChildContent of this shape was retrieved from the cache by the DisplayingAsync method above, as opposed to generated by the View Engine. - // ChildContent will be generated by the View Engine if it was not available in the cache when we rendered the shape. - // In this instance, we need insert the ChildContent into the cache so that subsequent attempt to render this shape can take advantage of the cached content. + // If we have got this far, then this shape is configured to be cached. + // We need to determine whether or not the ChildContent of this shape was retrieved from the cache by the DisplayingAsync method above, as opposed to generated by the View Engine. + // ChildContent will be generated by the View Engine if it was not available in the cache when we rendered the shape. + // In this instance, we need insert the ChildContent into the cache so that subsequent attempt to render this shape can take advantage of the cached content. - // If the ChildContent was retrieved form the cache, then the Cache Context will be present in the _cached collection (see the DisplayingAsync method in this class). - // So, if the cache context is not present in the _cached collection, we need to insert the ChildContent value into the cache: - if (!_cached.ContainsKey(cacheContext.CacheId) && context.ChildContent != null) - { - // The content is pre-encoded in the cache so we don't have to do it every time it's rendered. - using var sw = new ZStringWriter(); + // If the ChildContent was retrieved form the cache, then the Cache Context will be present in the _cached collection (see the DisplayingAsync method in this class). + // So, if the cache context is not present in the _cached collection, we need to insert the ChildContent value into the cache: + if (!_cached.ContainsKey(cacheContext.CacheId) && context.ChildContent != null) + { + // The content is pre-encoded in the cache so we don't have to do it every time it's rendered. + using var sw = new ZStringWriter(); - // 'ChildContent' may be a 'ViewBufferTextWriterContent' on which we can't - // call 'WriteTo()' twice, so here we update it with a new 'HtmlString()'. - context.ChildContent.WriteTo(sw, _htmlEncoder); - var contentHtmlString = new HtmlString(sw.ToString()); - context.ChildContent = contentHtmlString; + // 'ChildContent' may be a 'ViewBufferTextWriterContent' on which we can't + // call 'WriteTo()' twice, so here we update it with a new 'HtmlString()'. + context.ChildContent.WriteTo(sw, _htmlEncoder); + var contentHtmlString = new HtmlString(sw.ToString()); + context.ChildContent = contentHtmlString; - await _dynamicCacheService.SetCachedValueAsync(cacheContext, contentHtmlString.Value); - } + await _dynamicCacheService.SetCachedValueAsync(cacheContext, contentHtmlString.Value); } + } - public Task DisplayingFinalizedAsync(ShapeDisplayContext context) + public Task DisplayingFinalizedAsync(ShapeDisplayContext context) + { + if (!_cacheOptions.Enabled) { - if (!_cacheOptions.Enabled) - { - return Task.CompletedTask; - } - - var cacheContext = context.Shape.Metadata.Cache(); + return Task.CompletedTask; + } - if (cacheContext != null && _openScopes.ContainsKey(cacheContext.CacheId)) - { - _cacheScopeManager.ExitScope(); - _openScopes.Remove(cacheContext.CacheId); - } + var cacheContext = context.Shape.Metadata.Cache(); - return Task.CompletedTask; + if (cacheContext != null && _openScopes.ContainsKey(cacheContext.CacheId)) + { + _cacheScopeManager.ExitScope(); + _openScopes.Remove(cacheContext.CacheId); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Models/CacheContextModel.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Models/CacheContextModel.cs index a41d4c18940..6ec1c077997 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Models/CacheContextModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Models/CacheContextModel.cs @@ -3,55 +3,54 @@ using System.Linq; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache.Models +namespace OrchardCore.DynamicCache.Models; + +/// +/// A serialiazable and deserializable model to represent a . +/// +public class CacheContextModel { - /// - /// A serialiazable and deserializable model to represent a . - /// - public class CacheContextModel + public string CacheId { get; set; } + public ICollection Contexts { get; set; } + public IEnumerable Tags { get; set; } + public DateTimeOffset? ExpiresOn { get; set; } + public TimeSpan? ExpiresAfter { get; set; } + public TimeSpan? ExpiresSliding { get; set; } + + internal static CacheContextModel FromCacheContext(CacheContext cacheContext) { - public string CacheId { get; set; } - public ICollection Contexts { get; set; } - public IEnumerable Tags { get; set; } - public DateTimeOffset? ExpiresOn { get; set; } - public TimeSpan? ExpiresAfter { get; set; } - public TimeSpan? ExpiresSliding { get; set; } - - internal static CacheContextModel FromCacheContext(CacheContext cacheContext) + return new CacheContextModel { - return new CacheContextModel - { - CacheId = cacheContext.CacheId, - Contexts = cacheContext.Contexts, - Tags = cacheContext.Tags, - ExpiresOn = cacheContext.ExpiresOn, - ExpiresAfter = cacheContext.ExpiresAfter, - ExpiresSliding = cacheContext.ExpiresSliding - }; + CacheId = cacheContext.CacheId, + Contexts = cacheContext.Contexts, + Tags = cacheContext.Tags, + ExpiresOn = cacheContext.ExpiresOn, + ExpiresAfter = cacheContext.ExpiresAfter, + ExpiresSliding = cacheContext.ExpiresSliding + }; + } + + internal CacheContext ToCacheContext() + { + var cacheContext = new CacheContext(CacheId) + .AddContext(Contexts.ToArray()) + .AddTag(Tags.ToArray()); + + if (ExpiresOn.HasValue) + { + cacheContext.WithExpiryOn(ExpiresOn.Value); } - internal CacheContext ToCacheContext() + if (ExpiresAfter.HasValue) { - var cacheContext = new CacheContext(CacheId) - .AddContext(Contexts.ToArray()) - .AddTag(Tags.ToArray()); - - if (ExpiresOn.HasValue) - { - cacheContext.WithExpiryOn(ExpiresOn.Value); - } - - if (ExpiresAfter.HasValue) - { - cacheContext.WithExpiryAfter(ExpiresAfter.Value); - } - - if (ExpiresSliding.HasValue) - { - cacheContext.WithExpirySliding(ExpiresSliding.Value); - } - - return cacheContext; + cacheContext.WithExpiryAfter(ExpiresAfter.Value); } + + if (ExpiresSliding.HasValue) + { + cacheContext.WithExpirySliding(ExpiresSliding.Value); + } + + return cacheContext; } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCache.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCache.cs index 6c4292bf2aa..5856db07910 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCache.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCache.cs @@ -1,30 +1,29 @@ using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; -namespace OrchardCore.DynamicCache.Services +namespace OrchardCore.DynamicCache.Services; + +public class DefaultDynamicCache : IDynamicCache { - public class DefaultDynamicCache : IDynamicCache - { - private readonly IDistributedCache _distributedCache; + private readonly IDistributedCache _distributedCache; - public DefaultDynamicCache(IDistributedCache distributedCache) - { - _distributedCache = distributedCache; - } + public DefaultDynamicCache(IDistributedCache distributedCache) + { + _distributedCache = distributedCache; + } - public Task GetAsync(string key) - { - return _distributedCache.GetAsync(key); - } + public Task GetAsync(string key) + { + return _distributedCache.GetAsync(key); + } - public Task RemoveAsync(string key) - { - return _distributedCache.RemoveAsync(key); - } + public Task RemoveAsync(string key) + { + return _distributedCache.RemoveAsync(key); + } - public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options) - { - return _distributedCache.SetAsync(key, value, options); - } + public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options) + { + return _distributedCache.SetAsync(key, value, options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCacheService.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCacheService.cs index f9bfd7c6ed5..abffe5ca908 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCacheService.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Services/DefaultDynamicCacheService.cs @@ -13,198 +13,197 @@ using OrchardCore.DynamicCache.Models; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache.Services +namespace OrchardCore.DynamicCache.Services; + +public class DefaultDynamicCacheService : IDynamicCacheService { - public class DefaultDynamicCacheService : IDynamicCacheService + public const string FailoverKey = "OrchardCore_DynamicCache_FailoverKey"; + public static readonly TimeSpan DefaultFailoverRetryLatency = TimeSpan.FromSeconds(30); + + private readonly ICacheContextManager _cacheContextManager; + private readonly IDynamicCache _dynamicCache; + private readonly IMemoryCache _memoryCache; + private readonly IServiceProvider _serviceProvider; + private readonly DynamicCacheOptions _dynamicCacheOptions; + private readonly CacheOptions _cacheOptions; + private readonly ILogger _logger; + + private readonly Dictionary _localCache = []; + private ITagCache _tagcache; + + public DefaultDynamicCacheService( + ICacheContextManager cacheContextManager, + IDynamicCache dynamicCache, + IMemoryCache memoryCache, + IServiceProvider serviceProvider, + IOptions dynamicCacheOptions, + IOptions options, + ILogger logger) { - public const string FailoverKey = "OrchardCore_DynamicCache_FailoverKey"; - public static readonly TimeSpan DefaultFailoverRetryLatency = TimeSpan.FromSeconds(30); - - private readonly ICacheContextManager _cacheContextManager; - private readonly IDynamicCache _dynamicCache; - private readonly IMemoryCache _memoryCache; - private readonly IServiceProvider _serviceProvider; - private readonly DynamicCacheOptions _dynamicCacheOptions; - private readonly CacheOptions _cacheOptions; - private readonly ILogger _logger; - - private readonly Dictionary _localCache = []; - private ITagCache _tagcache; - - public DefaultDynamicCacheService( - ICacheContextManager cacheContextManager, - IDynamicCache dynamicCache, - IMemoryCache memoryCache, - IServiceProvider serviceProvider, - IOptions dynamicCacheOptions, - IOptions options, - ILogger logger) + _cacheContextManager = cacheContextManager; + _dynamicCache = dynamicCache; + _memoryCache = memoryCache; + _serviceProvider = serviceProvider; + _dynamicCacheOptions = dynamicCacheOptions.Value; + _dynamicCacheOptions.FailoverRetryLatency ??= DefaultFailoverRetryLatency; + _cacheOptions = options.Value; + _logger = logger; + } + + public async Task GetCachedValueAsync(CacheContext context) + { + if (!_cacheOptions.Enabled) { - _cacheContextManager = cacheContextManager; - _dynamicCache = dynamicCache; - _memoryCache = memoryCache; - _serviceProvider = serviceProvider; - _dynamicCacheOptions = dynamicCacheOptions.Value; - _dynamicCacheOptions.FailoverRetryLatency ??= DefaultFailoverRetryLatency; - _cacheOptions = options.Value; - _logger = logger; + return null; } - public async Task GetCachedValueAsync(CacheContext context) - { - if (!_cacheOptions.Enabled) - { - return null; - } + var cacheKey = await GetCacheKey(context); - var cacheKey = await GetCacheKey(context); + context = await GetCachedContextAsync(cacheKey); + if (context == null) + { + // We don't know the context, so we must treat this as a cache miss + return null; + } - context = await GetCachedContextAsync(cacheKey); - if (context == null) - { - // We don't know the context, so we must treat this as a cache miss - return null; - } + var content = await GetCachedStringAsync(cacheKey); - var content = await GetCachedStringAsync(cacheKey); + return content; + } - return content; + public async Task SetCachedValueAsync(CacheContext context, string value) + { + if (!_cacheOptions.Enabled) + { + return; } - public async Task SetCachedValueAsync(CacheContext context, string value) - { - if (!_cacheOptions.Enabled) - { - return; - } + var cacheKey = await GetCacheKey(context); - var cacheKey = await GetCacheKey(context); + _localCache[cacheKey] = value; + var esi = JConvert.SerializeObject(CacheContextModel.FromCacheContext(context)); - _localCache[cacheKey] = value; - var esi = JConvert.SerializeObject(CacheContextModel.FromCacheContext(context)); + await Task.WhenAll( + SetCachedValueAsync(cacheKey, value, context), + SetCachedValueAsync(GetCacheContextCacheKey(cacheKey), esi, context) + ); + } - await Task.WhenAll( - SetCachedValueAsync(cacheKey, value, context), - SetCachedValueAsync(GetCacheContextCacheKey(cacheKey), esi, context) - ); - } + public Task TagRemovedAsync(string tag, IEnumerable keys) + { + return Task.WhenAll(keys.Select(key => _dynamicCache.RemoveAsync(key))); + } - public Task TagRemovedAsync(string tag, IEnumerable keys) + private async Task SetCachedValueAsync(string cacheKey, string value, CacheContext context) + { + var failover = _memoryCache.Get(FailoverKey); + if (failover) { - return Task.WhenAll(keys.Select(key => _dynamicCache.RemoveAsync(key))); + return; } - private async Task SetCachedValueAsync(string cacheKey, string value, CacheContext context) + var bytes = Encoding.UTF8.GetBytes(value); + + var options = new DistributedCacheEntryOptions { - var failover = _memoryCache.Get(FailoverKey); - if (failover) - { - return; - } + AbsoluteExpiration = context.ExpiresOn, + SlidingExpiration = context.ExpiresSliding, + AbsoluteExpirationRelativeToNow = context.ExpiresAfter + }; - var bytes = Encoding.UTF8.GetBytes(value); + // Default duration is sliding expiration (permanent as long as it's used) + if (!options.AbsoluteExpiration.HasValue && !options.SlidingExpiration.HasValue && !options.AbsoluteExpirationRelativeToNow.HasValue) + { + options.SlidingExpiration = new TimeSpan(0, 1, 0); + } - var options = new DistributedCacheEntryOptions - { - AbsoluteExpiration = context.ExpiresOn, - SlidingExpiration = context.ExpiresSliding, - AbsoluteExpirationRelativeToNow = context.ExpiresAfter - }; + try + { + await _dynamicCache.SetAsync(cacheKey, bytes, options); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to write the '{CacheKey}' to the dynamic cache", cacheKey); - // Default duration is sliding expiration (permanent as long as it's used) - if (!options.AbsoluteExpiration.HasValue && !options.SlidingExpiration.HasValue && !options.AbsoluteExpirationRelativeToNow.HasValue) + _memoryCache.Set(FailoverKey, true, new MemoryCacheEntryOptions() { - options.SlidingExpiration = new TimeSpan(0, 1, 0); - } + AbsoluteExpirationRelativeToNow = _dynamicCacheOptions.FailoverRetryLatency + }); - try - { - await _dynamicCache.SetAsync(cacheKey, bytes, options); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to write the '{CacheKey}' to the dynamic cache", cacheKey); + return; + } - _memoryCache.Set(FailoverKey, true, new MemoryCacheEntryOptions() - { - AbsoluteExpirationRelativeToNow = _dynamicCacheOptions.FailoverRetryLatency - }); + // Lazy load to prevent cyclic dependency + _tagcache ??= _serviceProvider.GetRequiredService(); + await _tagcache.TagAsync(cacheKey, context.Tags.ToArray()); + } - return; - } + private async Task GetCacheKey(CacheContext context) + { + var cacheEntries = context.Contexts.Count > 0 + ? await _cacheContextManager.GetDiscriminatorsAsync(context.Contexts) + : []; - // Lazy load to prevent cyclic dependency - _tagcache ??= _serviceProvider.GetRequiredService(); - await _tagcache.TagAsync(cacheKey, context.Tags.ToArray()); + if (!cacheEntries.Any()) + { + return context.CacheId; } - private async Task GetCacheKey(CacheContext context) - { - var cacheEntries = context.Contexts.Count > 0 - ? await _cacheContextManager.GetDiscriminatorsAsync(context.Contexts) - : []; + var key = context.CacheId + "/" + cacheEntries.GetContextHash(); + return key; + } - if (!cacheEntries.Any()) - { - return context.CacheId; - } + private static string GetCacheContextCacheKey(string cacheKey) + { + return "cachecontext-" + cacheKey; + } - var key = context.CacheId + "/" + cacheEntries.GetContextHash(); - return key; + private async Task GetCachedStringAsync(string cacheKey) + { + if (_localCache.TryGetValue(cacheKey, out var content)) + { + return content; } - private static string GetCacheContextCacheKey(string cacheKey) + var failover = _memoryCache.Get(FailoverKey); + if (failover) { - return "cachecontext-" + cacheKey; + return null; } - private async Task GetCachedStringAsync(string cacheKey) + try { - if (_localCache.TryGetValue(cacheKey, out var content)) - { - return content; - } - - var failover = _memoryCache.Get(FailoverKey); - if (failover) + var bytes = await _dynamicCache.GetAsync(cacheKey); + if (bytes == null) { return null; } - try - { - var bytes = await _dynamicCache.GetAsync(cacheKey); - if (bytes == null) - { - return null; - } + return Encoding.UTF8.GetString(bytes); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to read the '{CacheKey}' from the dynamic cache", cacheKey); - return Encoding.UTF8.GetString(bytes); - } - catch (Exception e) + _memoryCache.Set(FailoverKey, true, new MemoryCacheEntryOptions() { - _logger.LogError(e, "Failed to read the '{CacheKey}' from the dynamic cache", cacheKey); - - _memoryCache.Set(FailoverKey, true, new MemoryCacheEntryOptions() - { - AbsoluteExpirationRelativeToNow = _dynamicCacheOptions.FailoverRetryLatency - }); - } - - return null; + AbsoluteExpirationRelativeToNow = _dynamicCacheOptions.FailoverRetryLatency + }); } - private async Task GetCachedContextAsync(string cacheKey) - { - var cachedValue = await GetCachedStringAsync(GetCacheContextCacheKey(cacheKey)); + return null; + } - if (cachedValue == null) - { - return null; - } + private async Task GetCachedContextAsync(string cacheKey) + { + var cachedValue = await GetCachedStringAsync(GetCacheContextCacheKey(cacheKey)); - var esiModel = JConvert.DeserializeObject(cachedValue); - return esiModel.ToCacheContext(); + if (cachedValue == null) + { + return null; } + + var esiModel = JConvert.DeserializeObject(cachedValue); + return esiModel.ToCacheContext(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Startup.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Startup.cs index 99e4cf007c1..5d939477f5a 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/Startup.cs @@ -9,35 +9,34 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; -namespace OrchardCore.DynamicCache +namespace OrchardCore.DynamicCache; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase - { - private readonly IShellConfiguration _shellConfiguration; + private readonly IShellConfiguration _shellConfiguration; - public Startup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + public Startup(IShellConfiguration shellConfiguration) + { + _shellConfiguration = shellConfiguration; + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(); - services.AddShapeAttributes(); + services.AddScoped(); + services.AddShapeAttributes(); - services.AddSingleton(); - services.AddSingleton(); - services.AddTagHelpers(); - services.AddTagHelpers(); + services.AddSingleton(); + services.AddSingleton(); + services.AddTagHelpers(); + services.AddTagHelpers(); - services.AddTransient, CacheOptionsConfiguration>(); - services.Configure(_shellConfiguration.GetSection("OrchardCore_DynamicCache")); - } + services.AddTransient, CacheOptionsConfiguration>(); + services.Configure(_shellConfiguration.GetSection("OrchardCore_DynamicCache")); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/CacheDependencyTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/CacheDependencyTagHelper.cs index b055b279399..765c51a971c 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/CacheDependencyTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/CacheDependencyTagHelper.cs @@ -2,43 +2,42 @@ using Microsoft.AspNetCore.Razor.TagHelpers; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache.TagHelpers -{ - [HtmlTargetElement("cache-dependency", Attributes = DependencyAttributeName)] - public class CacheDependencyTagHelper : TagHelper - { - private const string DependencyAttributeName = "dependency"; +namespace OrchardCore.DynamicCache.TagHelpers; - /// - /// Gets or sets a with the dependency to invalidate the cache with. - /// - [HtmlAttributeName(DependencyAttributeName)] - public string Dependency { get; set; } +[HtmlTargetElement("cache-dependency", Attributes = DependencyAttributeName)] +public class CacheDependencyTagHelper : TagHelper +{ + private const string DependencyAttributeName = "dependency"; - private readonly ICacheScopeManager _cacheScopeManager; + /// + /// Gets or sets a with the dependency to invalidate the cache with. + /// + [HtmlAttributeName(DependencyAttributeName)] + public string Dependency { get; set; } - public CacheDependencyTagHelper( - ICacheScopeManager cacheScopeManager) + private readonly ICacheScopeManager _cacheScopeManager; - { - _cacheScopeManager = cacheScopeManager; - } + public CacheDependencyTagHelper( + ICacheScopeManager cacheScopeManager) + { + _cacheScopeManager = cacheScopeManager; + } - /// - public override void Process(TagHelperContext context, TagHelperOutput output) - { - ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(output); + /// + public override void Process(TagHelperContext context, TagHelperOutput output) + { + ArgumentNullException.ThrowIfNull(context); - if (!string.IsNullOrEmpty(Dependency)) - { - _cacheScopeManager.AddDependencies(Dependency); - } + ArgumentNullException.ThrowIfNull(output); - // Clear the contents of the "cache-dependency" element since we don't want to render it. - output.SuppressOutput(); + if (!string.IsNullOrEmpty(Dependency)) + { + _cacheScopeManager.AddDependencies(Dependency); } + + // Clear the contents of the "cache-dependency" element since we don't want to render it. + output.SuppressOutput(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelper.cs index b0e1289e333..e5193bbcc88 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelper.cs @@ -8,262 +8,261 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Cache; -namespace OrchardCore.DynamicCache.TagHelpers +namespace OrchardCore.DynamicCache.TagHelpers; + +[HtmlTargetElement("dynamic-cache", Attributes = CacheIdAttributeName)] +public class DynamicCacheTagHelper : TagHelper { - [HtmlTargetElement("dynamic-cache", Attributes = CacheIdAttributeName)] - public class DynamicCacheTagHelper : TagHelper + private const string CacheIdAttributeName = "cache-id"; + private const string VaryByAttributeName = "vary-by"; + private const string DependenciesAttributeNAme = "dependencies"; + private const string ExpiresOnAttributeName = "expires-on"; + private const string ExpiresAfterAttributeName = "expires-after"; + private const string ExpiresSlidingAttributeName = "expires-sliding"; + private const string EnabledAttributeName = "enabled"; + + private static readonly char[] _splitChars = [',', ' ']; + + /// + /// The default duration, from the time the cache entry was added, when it should be evicted. + /// This default duration will only be used if no other expiration criteria is specified. + /// The default expiration time is a sliding expiration of 30 seconds. + /// + public static readonly TimeSpan DefaultExpiration = TimeSpan.FromSeconds(30); + + /// + /// Gets the which encodes the content to be cached. + /// + protected HtmlEncoder HtmlEncoder { get; } + + /// + /// Gets or sets a identifying this cache entry. + /// + [HtmlAttributeName(CacheIdAttributeName)] + public string CacheId { get; set; } + + /// + /// Gets or sets a to vary the cached result by. + /// + [HtmlAttributeName(VaryByAttributeName)] + public string VaryBy { get; set; } + + /// + /// Gets or sets a with the dependencies to invalidate the cache with. + /// + [HtmlAttributeName(DependenciesAttributeNAme)] + public string Dependencies { get; set; } + + /// + /// Gets or sets the exact the cache entry should be evicted. + /// + [HtmlAttributeName(ExpiresOnAttributeName)] + public DateTimeOffset? ExpiresOn { get; set; } + + /// + /// Gets or sets the duration, from the time the cache entry was added, when it should be evicted. + /// + [HtmlAttributeName(ExpiresAfterAttributeName)] + public TimeSpan? ExpiresAfter { get; set; } + + /// + /// Gets or sets the duration from last access that the cache entry should be evicted. + /// + [HtmlAttributeName(ExpiresSlidingAttributeName)] + public TimeSpan? ExpiresSliding { get; set; } + + /// + /// Gets or sets the value which determines if the tag helper is enabled or not. + /// + [HtmlAttributeName(EnabledAttributeName)] + public bool Enabled { get; set; } = true; + + /// + /// Prefix used by instances when creating entries in . + /// + public static readonly string CacheKeyPrefix = nameof(DynamicCacheTagHelper); + + private readonly IDynamicCacheService _dynamicCacheService; + private readonly ICacheScopeManager _cacheScopeManager; + private readonly DynamicCacheTagHelperService _dynamicCacheTagHelperService; + private readonly CacheOptions _cacheOptions; + + public DynamicCacheTagHelper( + IDynamicCacheService dynamicCacheService, + ICacheScopeManager cacheScopeManager, + HtmlEncoder htmlEncoder, + DynamicCacheTagHelperService dynamicCacheTagHelperService, + IOptions cacheOptions) { - private const string CacheIdAttributeName = "cache-id"; - private const string VaryByAttributeName = "vary-by"; - private const string DependenciesAttributeNAme = "dependencies"; - private const string ExpiresOnAttributeName = "expires-on"; - private const string ExpiresAfterAttributeName = "expires-after"; - private const string ExpiresSlidingAttributeName = "expires-sliding"; - private const string EnabledAttributeName = "enabled"; - - private static readonly char[] _splitChars = [',', ' ']; - - /// - /// The default duration, from the time the cache entry was added, when it should be evicted. - /// This default duration will only be used if no other expiration criteria is specified. - /// The default expiration time is a sliding expiration of 30 seconds. - /// - public static readonly TimeSpan DefaultExpiration = TimeSpan.FromSeconds(30); - - /// - /// Gets the which encodes the content to be cached. - /// - protected HtmlEncoder HtmlEncoder { get; } - - /// - /// Gets or sets a identifying this cache entry. - /// - [HtmlAttributeName(CacheIdAttributeName)] - public string CacheId { get; set; } - - /// - /// Gets or sets a to vary the cached result by. - /// - [HtmlAttributeName(VaryByAttributeName)] - public string VaryBy { get; set; } - - /// - /// Gets or sets a with the dependencies to invalidate the cache with. - /// - [HtmlAttributeName(DependenciesAttributeNAme)] - public string Dependencies { get; set; } - - /// - /// Gets or sets the exact the cache entry should be evicted. - /// - [HtmlAttributeName(ExpiresOnAttributeName)] - public DateTimeOffset? ExpiresOn { get; set; } - - /// - /// Gets or sets the duration, from the time the cache entry was added, when it should be evicted. - /// - [HtmlAttributeName(ExpiresAfterAttributeName)] - public TimeSpan? ExpiresAfter { get; set; } - - /// - /// Gets or sets the duration from last access that the cache entry should be evicted. - /// - [HtmlAttributeName(ExpiresSlidingAttributeName)] - public TimeSpan? ExpiresSliding { get; set; } - - /// - /// Gets or sets the value which determines if the tag helper is enabled or not. - /// - [HtmlAttributeName(EnabledAttributeName)] - public bool Enabled { get; set; } = true; - - /// - /// Prefix used by instances when creating entries in . - /// - public static readonly string CacheKeyPrefix = nameof(DynamicCacheTagHelper); - - private readonly IDynamicCacheService _dynamicCacheService; - private readonly ICacheScopeManager _cacheScopeManager; - private readonly DynamicCacheTagHelperService _dynamicCacheTagHelperService; - private readonly CacheOptions _cacheOptions; - - public DynamicCacheTagHelper( - IDynamicCacheService dynamicCacheService, - ICacheScopeManager cacheScopeManager, - HtmlEncoder htmlEncoder, - DynamicCacheTagHelperService dynamicCacheTagHelperService, - IOptions cacheOptions) - { - _dynamicCacheService = dynamicCacheService; - _cacheScopeManager = cacheScopeManager; - HtmlEncoder = htmlEncoder; - _dynamicCacheTagHelperService = dynamicCacheTagHelperService; - _cacheOptions = cacheOptions.Value; - } + _dynamicCacheService = dynamicCacheService; + _cacheScopeManager = cacheScopeManager; + HtmlEncoder = htmlEncoder; + _dynamicCacheTagHelperService = dynamicCacheTagHelperService; + _cacheOptions = cacheOptions.Value; + } - /// - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - ArgumentNullException.ThrowIfNull(context); + /// + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + ArgumentNullException.ThrowIfNull(context); - ArgumentNullException.ThrowIfNull(output); + ArgumentNullException.ThrowIfNull(output); - IHtmlContent content; + IHtmlContent content; - if (Enabled) - { - var cacheContext = new CacheContext(CacheId); + if (Enabled) + { + var cacheContext = new CacheContext(CacheId); - if (!string.IsNullOrEmpty(VaryBy)) - { - cacheContext.AddContext(VaryBy.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries)); - } + if (!string.IsNullOrEmpty(VaryBy)) + { + cacheContext.AddContext(VaryBy.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries)); + } - if (!string.IsNullOrEmpty(Dependencies)) - { - cacheContext.AddTag(Dependencies.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries)); - } + if (!string.IsNullOrEmpty(Dependencies)) + { + cacheContext.AddTag(Dependencies.Split(_splitChars, StringSplitOptions.RemoveEmptyEntries)); + } - var hasEvictionCriteria = false; + var hasEvictionCriteria = false; - if (ExpiresOn.HasValue) - { - hasEvictionCriteria = true; - cacheContext.WithExpiryOn(ExpiresOn.Value); - } + if (ExpiresOn.HasValue) + { + hasEvictionCriteria = true; + cacheContext.WithExpiryOn(ExpiresOn.Value); + } - if (ExpiresAfter.HasValue) - { - hasEvictionCriteria = true; - cacheContext.WithExpiryAfter(ExpiresAfter.Value); - } + if (ExpiresAfter.HasValue) + { + hasEvictionCriteria = true; + cacheContext.WithExpiryAfter(ExpiresAfter.Value); + } - if (ExpiresSliding.HasValue) - { - hasEvictionCriteria = true; - cacheContext.WithExpirySliding(ExpiresSliding.Value); - } + if (ExpiresSliding.HasValue) + { + hasEvictionCriteria = true; + cacheContext.WithExpirySliding(ExpiresSliding.Value); + } - if (!hasEvictionCriteria) - { - cacheContext.WithExpirySliding(DefaultExpiration); - } + if (!hasEvictionCriteria) + { + cacheContext.WithExpirySliding(DefaultExpiration); + } - _cacheScopeManager.EnterScope(cacheContext); + _cacheScopeManager.EnterScope(cacheContext); - try - { - content = await ProcessContentAsync(output, cacheContext); - } - finally - { - _cacheScopeManager.ExitScope(); - } + try + { + content = await ProcessContentAsync(output, cacheContext); } - else + finally { - content = await output.GetChildContentAsync(); + _cacheScopeManager.ExitScope(); } + } + else + { + content = await output.GetChildContentAsync(); + } - // Clear the contents of the "cache" element since we don't want to render it. - output.SuppressOutput(); + // Clear the contents of the "cache" element since we don't want to render it. + output.SuppressOutput(); - output.Content.SetHtmlContent(content); - } + output.Content.SetHtmlContent(content); + } + + public async Task ProcessContentAsync(TagHelperOutput output, CacheContext cacheContext) + { + IHtmlContent content = null; - public async Task ProcessContentAsync(TagHelperOutput output, CacheContext cacheContext) + while (content == null) { - IHtmlContent content = null; + Task result; - while (content == null) + // Is there any request already processing the value? + if (!_dynamicCacheTagHelperService.Workers.TryGetValue(CacheId, out result)) { - Task result; + // There is a small race condition here between TryGetValue and TryAdd that might cause the + // content to be computed more than once. We don't care about this race as the probability of + // happening is very small and the impact is not critical. + var tcs = new TaskCompletionSource(); - // Is there any request already processing the value? - if (!_dynamicCacheTagHelperService.Workers.TryGetValue(CacheId, out result)) - { - // There is a small race condition here between TryGetValue and TryAdd that might cause the - // content to be computed more than once. We don't care about this race as the probability of - // happening is very small and the impact is not critical. - var tcs = new TaskCompletionSource(); + _dynamicCacheTagHelperService.Workers.TryAdd(CacheId, tcs.Task); - _dynamicCacheTagHelperService.Workers.TryAdd(CacheId, tcs.Task); + try + { + var value = await _dynamicCacheService.GetCachedValueAsync(cacheContext); - try + if (value == null) { - var value = await _dynamicCacheService.GetCachedValueAsync(cacheContext); + // The value is not cached, we need to render the tag helper output + var processedContent = await output.GetChildContentAsync(); - if (value == null) + using var writer = new ZStringWriter(); + // Write the start of a cache debug block. + if (_cacheOptions.DebugMode) { - // The value is not cached, we need to render the tag helper output - var processedContent = await output.GetChildContentAsync(); - - using var writer = new ZStringWriter(); - // Write the start of a cache debug block. - if (_cacheOptions.DebugMode) - { - // No need to optimize this code as it will be used for debugging purpose. - writer.WriteLine(); - writer.WriteLine($""); - } - - // Always write the content regardless of debug mode. - processedContent.WriteTo(writer, HtmlEncoder); - - // Write the end of a cache debug block. - if (_cacheOptions.DebugMode) - { - writer.WriteLine(); - writer.WriteLine($""); - } - - await writer.FlushAsync(); - - var html = writer.ToString(); - - var formattingContext = new DistributedCacheTagHelperFormattingContext - { - Html = new HtmlString(html) - }; - - await _dynamicCacheService.SetCachedValueAsync(cacheContext, html); - - content = formattingContext.Html; + // No need to optimize this code as it will be used for debugging purpose. + writer.WriteLine(); + writer.WriteLine($""); } - else + + // Always write the content regardless of debug mode. + processedContent.WriteTo(writer, HtmlEncoder); + + // Write the end of a cache debug block. + if (_cacheOptions.DebugMode) { - content = new HtmlString(value); + writer.WriteLine(); + writer.WriteLine($""); } + + await writer.FlushAsync(); + + var html = writer.ToString(); + + var formattingContext = new DistributedCacheTagHelperFormattingContext + { + Html = new HtmlString(html) + }; + + await _dynamicCacheService.SetCachedValueAsync(cacheContext, html); + + content = formattingContext.Html; } - catch - { - content = null; - throw; - } - finally + else { - // Remove the worker task before setting the result. - // If the result is null, other threads would potentially - // acquire it otherwise. - _dynamicCacheTagHelperService.Workers.TryRemove(CacheId, out _); - - // Notify all other awaiters to render the content - tcs.TrySetResult(content); + content = new HtmlString(value); } } - else + catch { - content = await result; + content = null; + throw; } - } + finally + { + // Remove the worker task before setting the result. + // If the result is null, other threads would potentially + // acquire it otherwise. + _dynamicCacheTagHelperService.Workers.TryRemove(CacheId, out _); - return content; + // Notify all other awaiters to render the content + tcs.TrySetResult(content); + } + } + else + { + content = await result; + } } + + return content; } } diff --git a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelperService.cs b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelperService.cs index 6ee1e34a770..7a7d54088a7 100644 --- a/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelperService.cs +++ b/src/OrchardCore.Modules/OrchardCore.DynamicCache/TagHelpers/DynamicCacheTagHelperService.cs @@ -2,10 +2,9 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Html; -namespace OrchardCore.DynamicCache.TagHelpers +namespace OrchardCore.DynamicCache.TagHelpers; + +public class DynamicCacheTagHelperService { - public class DynamicCacheTagHelperService - { - public ConcurrentDictionary> Workers = new(); - } + public ConcurrentDictionary> Workers = new(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Email.Azure/Models/DefaultAzureEmailOptions.cs b/src/OrchardCore.Modules/OrchardCore.Email.Azure/Models/DefaultAzureEmailOptions.cs index 4af8b72853d..cb9755a63b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email.Azure/Models/DefaultAzureEmailOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email.Azure/Models/DefaultAzureEmailOptions.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.Email.Azure.Models; +namespace OrchardCore.Email.Azure.Models; public class DefaultAzureEmailOptions : AzureEmailOptions { diff --git a/src/OrchardCore.Modules/OrchardCore.Email/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Email/AdminMenu.cs index d5d586aee02..884b7ad8ef6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/AdminMenu.cs @@ -6,51 +6,50 @@ using OrchardCore.Mvc.Core.Utilities; using OrchardCore.Navigation; -namespace OrchardCore.Email +namespace OrchardCore.Email; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", EmailSettings.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", EmailSettings.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Email"], S["Email"].PrefixPosition(), entry => entry - .AddClass("email") - .Id("email") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageEmailSettings) - .LocalNav() - ) - .Add(S["Email Test"], S["Email Test"].PrefixPosition(), entry => entry - .AddClass("emailtest") - .Id("emailtest") - .Action(nameof(AdminController.Test), typeof(AdminController).ControllerName(), "OrchardCore.Email") - .Permission(Permissions.ManageEmailSettings) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Email"], S["Email"].PrefixPosition(), entry => entry + .AddClass("email") + .Id("email") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageEmailSettings) + .LocalNav() ) - ); + .Add(S["Email Test"], S["Email Test"].PrefixPosition(), entry => entry + .AddClass("emailtest") + .Id("emailtest") + .Action(nameof(AdminController.Test), typeof(AdminController).ControllerName(), "OrchardCore.Email") + .Permission(Permissions.ManageEmailSettings) + .LocalNav() + ) + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Email/Extensions/OrchardCoreBuilderExtensions.cs index de377fc803e..0d57aaedfe6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Extensions/OrchardCoreBuilderExtensions.cs @@ -3,26 +3,25 @@ using OrchardCore.Email; using OrchardCore.Environment.Shell.Configuration; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + [Obsolete("This extension is now obsolete and will be removed in the next release. You can safely stop using it, but please keep providing valid settings in the configuration provider for continued functionality.")] + public static OrchardCoreBuilder ConfigureEmailSettings(this OrchardCoreBuilder builder) { - [Obsolete("This extension is now obsolete and will be removed in the next release. You can safely stop using it, but please keep providing valid settings in the configuration provider for continued functionality.")] - public static OrchardCoreBuilder ConfigureEmailSettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Email"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Email"); - tenantServices.Configure(options => - { - configurationSection.Bind(options); + tenantServices.Configure(options => + { + configurationSection.Bind(options); - options.IsEnabled = options.ConfigurationExists(); - }); + options.IsEnabled = options.ConfigurationExists(); }); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Email/Startup.cs index da1cd6b47ac..38c059baafb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Startup.cs @@ -9,18 +9,17 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Email +namespace OrchardCore.Email; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddEmailServices() - .AddScoped, EmailSettingsDisplayDriver>() - .AddScoped() - .AddScoped(); + services.AddEmailServices() + .AddScoped, EmailSettingsDisplayDriver>() + .AddScoped() + .AddScoped(); - services.AddDataMigration(); - } + services.AddDataMigration(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/EmailSettingsBaseViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/EmailSettingsBaseViewModel.cs index a6d688d989d..cdb0f02dc77 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/EmailSettingsBaseViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/ViewModels/EmailSettingsBaseViewModel.cs @@ -1,4 +1,4 @@ -namespace OrchardCore.Email.ViewModels; +namespace OrchardCore.Email.ViewModels; public class EmailSettingsBaseViewModel { diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Activities/EmailTask.cs b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Activities/EmailTask.cs index 0bcdd816b06..25819777245 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Activities/EmailTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Activities/EmailTask.cs @@ -7,131 +7,130 @@ using OrchardCore.Workflows.Models; using OrchardCore.Workflows.Services; -namespace OrchardCore.Email.Workflows.Activities +namespace OrchardCore.Email.Workflows.Activities; + +public class EmailTask : TaskActivity { - public class EmailTask : TaskActivity + private readonly IEmailService _emailService; + private readonly IWorkflowExpressionEvaluator _expressionEvaluator; + protected readonly IStringLocalizer S; + private readonly HtmlEncoder _htmlEncoder; + + public EmailTask( + IEmailService emailService, + IWorkflowExpressionEvaluator expressionEvaluator, + IStringLocalizer localizer, + HtmlEncoder htmlEncoder + ) { - private readonly IEmailService _emailService; - private readonly IWorkflowExpressionEvaluator _expressionEvaluator; - protected readonly IStringLocalizer S; - private readonly HtmlEncoder _htmlEncoder; - - public EmailTask( - IEmailService emailService, - IWorkflowExpressionEvaluator expressionEvaluator, - IStringLocalizer localizer, - HtmlEncoder htmlEncoder - ) - { - _emailService = emailService; - _expressionEvaluator = expressionEvaluator; - S = localizer; - _htmlEncoder = htmlEncoder; - } + _emailService = emailService; + _expressionEvaluator = expressionEvaluator; + S = localizer; + _htmlEncoder = htmlEncoder; + } - public override LocalizedString DisplayText => S["Email Task"]; + public override LocalizedString DisplayText => S["Email Task"]; - public override LocalizedString Category => S["Messaging"]; + public override LocalizedString Category => S["Messaging"]; - public WorkflowExpression Author - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Author + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression Sender - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Sender + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression ReplyTo - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression ReplyTo + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - // TODO: Add support for the following format: Jack Bauer, ... - public WorkflowExpression Recipients - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + // TODO: Add support for the following format: Jack Bauer, ... + public WorkflowExpression Recipients + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression Cc - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Cc + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression Bcc - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Bcc + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression Subject - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Subject + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public WorkflowExpression Body - { - get => GetProperty(() => new WorkflowExpression()); - set => SetProperty(value); - } + public WorkflowExpression Body + { + get => GetProperty(() => new WorkflowExpression()); + set => SetProperty(value); + } - public bool IsHtmlBody - { - get => GetProperty(() => true); - set => SetProperty(value); - } + public bool IsHtmlBody + { + get => GetProperty(() => true); + set => SetProperty(value); + } - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Failed"]); + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var author = await _expressionEvaluator.EvaluateAsync(Author, workflowContext, null); + var sender = await _expressionEvaluator.EvaluateAsync(Sender, workflowContext, null); + var replyTo = await _expressionEvaluator.EvaluateAsync(ReplyTo, workflowContext, null); + var recipients = await _expressionEvaluator.EvaluateAsync(Recipients, workflowContext, null); + var cc = await _expressionEvaluator.EvaluateAsync(Cc, workflowContext, null); + var bcc = await _expressionEvaluator.EvaluateAsync(Bcc, workflowContext, null); + var subject = await _expressionEvaluator.EvaluateAsync(Subject, workflowContext, null); + var body = await _expressionEvaluator.EvaluateAsync(Body, workflowContext, IsHtmlBody ? _htmlEncoder : null); + + var message = new MailMessage + { + // Author and Sender are both not required fields. + From = author?.Trim() ?? sender?.Trim(), + To = recipients?.Trim(), + Cc = cc?.Trim(), + Bcc = bcc?.Trim(), + // Email reply-to header https://tools.ietf.org/html/rfc4021#section-2.1.4 + ReplyTo = replyTo?.Trim(), + Subject = subject?.Trim(), + Body = body?.Trim(), + IsHtmlBody = IsHtmlBody + }; + + if (!string.IsNullOrWhiteSpace(sender)) { - return Outcomes(S["Done"], S["Failed"]); + message.Sender = sender.Trim(); } - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + var result = await _emailService.SendAsync(message); + workflowContext.LastResult = result; + + if (!result.Succeeded) { - var author = await _expressionEvaluator.EvaluateAsync(Author, workflowContext, null); - var sender = await _expressionEvaluator.EvaluateAsync(Sender, workflowContext, null); - var replyTo = await _expressionEvaluator.EvaluateAsync(ReplyTo, workflowContext, null); - var recipients = await _expressionEvaluator.EvaluateAsync(Recipients, workflowContext, null); - var cc = await _expressionEvaluator.EvaluateAsync(Cc, workflowContext, null); - var bcc = await _expressionEvaluator.EvaluateAsync(Bcc, workflowContext, null); - var subject = await _expressionEvaluator.EvaluateAsync(Subject, workflowContext, null); - var body = await _expressionEvaluator.EvaluateAsync(Body, workflowContext, IsHtmlBody ? _htmlEncoder : null); - - var message = new MailMessage - { - // Author and Sender are both not required fields. - From = author?.Trim() ?? sender?.Trim(), - To = recipients?.Trim(), - Cc = cc?.Trim(), - Bcc = bcc?.Trim(), - // Email reply-to header https://tools.ietf.org/html/rfc4021#section-2.1.4 - ReplyTo = replyTo?.Trim(), - Subject = subject?.Trim(), - Body = body?.Trim(), - IsHtmlBody = IsHtmlBody - }; - - if (!string.IsNullOrWhiteSpace(sender)) - { - message.Sender = sender.Trim(); - } - - var result = await _emailService.SendAsync(message); - workflowContext.LastResult = result; - - if (!result.Succeeded) - { - return Outcomes("Failed"); - } - - return Outcomes("Done"); + return Outcomes("Failed"); } + + return Outcomes("Done"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Drivers/EmailTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Drivers/EmailTaskDisplayDriver.cs index 1fcd992fcb5..a726c3bcc8d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Drivers/EmailTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Drivers/EmailTaskDisplayDriver.cs @@ -3,34 +3,33 @@ using OrchardCore.Workflows.Display; using OrchardCore.Workflows.Models; -namespace OrchardCore.Email.Workflows.Drivers +namespace OrchardCore.Email.Workflows.Drivers; + +public sealed class EmailTaskDisplayDriver : ActivityDisplayDriver { - public sealed class EmailTaskDisplayDriver : ActivityDisplayDriver + protected override void EditActivity(EmailTask activity, EmailTaskViewModel model) { - protected override void EditActivity(EmailTask activity, EmailTaskViewModel model) - { - model.SenderExpression = activity.Sender.Expression; - model.AuthorExpression = activity.Author.Expression; - model.RecipientsExpression = activity.Recipients.Expression; - model.ReplyToExpression = activity.ReplyTo.Expression; - model.SubjectExpression = activity.Subject.Expression; - model.Body = activity.Body.Expression; - model.IsHtmlBody = activity.IsHtmlBody; - model.BccExpression = activity.Bcc.Expression; - model.CcExpression = activity.Cc.Expression; - } + model.SenderExpression = activity.Sender.Expression; + model.AuthorExpression = activity.Author.Expression; + model.RecipientsExpression = activity.Recipients.Expression; + model.ReplyToExpression = activity.ReplyTo.Expression; + model.SubjectExpression = activity.Subject.Expression; + model.Body = activity.Body.Expression; + model.IsHtmlBody = activity.IsHtmlBody; + model.BccExpression = activity.Bcc.Expression; + model.CcExpression = activity.Cc.Expression; + } - protected override void UpdateActivity(EmailTaskViewModel model, EmailTask activity) - { - activity.Sender = new WorkflowExpression(model.SenderExpression); - activity.Author = new WorkflowExpression(model.AuthorExpression); - activity.Recipients = new WorkflowExpression(model.RecipientsExpression); - activity.ReplyTo = new WorkflowExpression(model.ReplyToExpression); - activity.Subject = new WorkflowExpression(model.SubjectExpression); - activity.Body = new WorkflowExpression(model.Body); - activity.IsHtmlBody = model.IsHtmlBody; - activity.Bcc = new WorkflowExpression(model.BccExpression); - activity.Cc = new WorkflowExpression(model.CcExpression); - } + protected override void UpdateActivity(EmailTaskViewModel model, EmailTask activity) + { + activity.Sender = new WorkflowExpression(model.SenderExpression); + activity.Author = new WorkflowExpression(model.AuthorExpression); + activity.Recipients = new WorkflowExpression(model.RecipientsExpression); + activity.ReplyTo = new WorkflowExpression(model.ReplyToExpression); + activity.Subject = new WorkflowExpression(model.SubjectExpression); + activity.Body = new WorkflowExpression(model.Body); + activity.IsHtmlBody = model.IsHtmlBody; + activity.Bcc = new WorkflowExpression(model.BccExpression); + activity.Cc = new WorkflowExpression(model.CcExpression); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Startup.cs index 286102aa921..600298fc90f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/Startup.cs @@ -4,14 +4,13 @@ using OrchardCore.Modules; using OrchardCore.Workflows.Helpers; -namespace OrchardCore.Email.Workflows +namespace OrchardCore.Email.Workflows; + +[RequireFeatures("OrchardCore.Workflows")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Workflows")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddActivity(); - } + services.AddActivity(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/ViewModels/EmailTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/ViewModels/EmailTaskViewModel.cs index 1b4adf4bb4e..f6dab9bb6f4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Email/Workflows/ViewModels/EmailTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Email/Workflows/ViewModels/EmailTaskViewModel.cs @@ -1,23 +1,22 @@ -namespace OrchardCore.Email.Workflows.ViewModels +namespace OrchardCore.Email.Workflows.ViewModels; + +public class EmailTaskViewModel { - public class EmailTaskViewModel - { - public string AuthorExpression { get; set; } + public string AuthorExpression { get; set; } - public string SenderExpression { get; set; } + public string SenderExpression { get; set; } - public string ReplyToExpression { get; set; } + public string ReplyToExpression { get; set; } - public string CcExpression { get; set; } + public string CcExpression { get; set; } - public string BccExpression { get; set; } + public string BccExpression { get; set; } - public string RecipientsExpression { get; set; } + public string RecipientsExpression { get; set; } - public string SubjectExpression { get; set; } + public string SubjectExpression { get; set; } - public string Body { get; set; } + public string Body { get; set; } - public bool IsHtmlBody { get; set; } - } + public bool IsHtmlBody { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentSource.cs index 31ee2c126f2..8b470e441d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentSource.cs @@ -3,35 +3,34 @@ using OrchardCore.Deployment; using OrchardCore.Facebook.Services; -namespace OrchardCore.Facebook.Deployment +namespace OrchardCore.Facebook.Deployment; + +public class FacebookLoginDeploymentSource : IDeploymentSource { - public class FacebookLoginDeploymentSource : IDeploymentSource + private readonly IFacebookService _facebookService; + + public FacebookLoginDeploymentSource(IFacebookService facebookService) { - private readonly IFacebookService _facebookService; + _facebookService = facebookService; + } - public FacebookLoginDeploymentSource(IFacebookService facebookService) - { - _facebookService = facebookService; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var facebookLoginStep = step as FacebookLoginDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (facebookLoginStep == null) { - var facebookLoginStep = step as FacebookLoginDeploymentStep; - - if (facebookLoginStep == null) - { - return; - } + return; + } - var settings = await _facebookService.GetSettingsAsync(); + var settings = await _facebookService.GetSettingsAsync(); - // The 'name' property should match the related recipe step name. - var jObject = new JsonObject { ["name"] = "FacebookCoreSettings" }; + // The 'name' property should match the related recipe step name. + var jObject = new JsonObject { ["name"] = "FacebookCoreSettings" }; - // Merge settings as the recipe step doesn't use a child property. - jObject.Merge(JObject.FromObject(settings)); + // Merge settings as the recipe step doesn't use a child property. + jObject.Merge(JObject.FromObject(settings)); - result.Steps.Add(jObject); - } + result.Steps.Add(jObject); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStep.cs index 8e5a29783d3..3ff6e42d76c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Facebook.Deployment +namespace OrchardCore.Facebook.Deployment; + +/// +/// Adds Facebook Login settings to a . +/// +public class FacebookLoginDeploymentStep : DeploymentStep { - /// - /// Adds Facebook Login settings to a . - /// - public class FacebookLoginDeploymentStep : DeploymentStep + public FacebookLoginDeploymentStep() { - public FacebookLoginDeploymentStep() - { - Name = "Facebook Login"; - } + Name = "Facebook Login"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStepDriver.cs index 491973c907e..7569a70e257 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Deployment/FacebookLoginDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Facebook.Deployment +namespace OrchardCore.Facebook.Deployment; + +public sealed class FacebookLoginDeploymentStepDriver : DisplayDriver { - public sealed class FacebookLoginDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(FacebookLoginDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(FacebookLoginDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("FacebookLoginDeploymentStep_Summary", step).Location("Summary", "Content"), - View("FacebookLoginDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("FacebookLoginDeploymentStep_Summary", step).Location("Summary", "Content"), + View("FacebookLoginDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(FacebookLoginDeploymentStep step, BuildEditorContext context) - { - return View("FacebookLoginDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(FacebookLoginDeploymentStep step, BuildEditorContext context) + { + return View("FacebookLoginDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Drivers/FacebookSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Drivers/FacebookSettingsDisplayDriver.cs index b0d36f818b1..0b5f0ef6331 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Drivers/FacebookSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Drivers/FacebookSettingsDisplayDriver.cs @@ -12,99 +12,98 @@ using OrchardCore.Facebook.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Facebook.Drivers +namespace OrchardCore.Facebook.Drivers; + +public sealed class FacebookSettingsDisplayDriver : SiteDisplayDriver { - public sealed class FacebookSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public FacebookSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IDataProtectionProvider dataProtectionProvider, + IHttpContextAccessor httpContextAccessor, + ILogger logger + ) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _dataProtectionProvider = dataProtectionProvider; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + protected override string SettingsGroupId + => FacebookConstants.Features.Core; - public FacebookSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IDataProtectionProvider dataProtectionProvider, - IHttpContextAccessor httpContextAccessor, - ILogger logger - ) + public override async Task EditAsync(ISite site, FacebookSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _dataProtectionProvider = dataProtectionProvider; - _httpContextAccessor = httpContextAccessor; - _logger = logger; + return null; } - protected override string SettingsGroupId - => FacebookConstants.Features.Core; - - public override async Task EditAsync(ISite site, FacebookSettings settings, BuildEditorContext context) + return Initialize("FacebookSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) - { - return null; - } + var protector = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core); - return Initialize("FacebookSettings_Edit", model => + model.AppId = settings.AppId; + model.FBInit = settings.FBInit; + model.FBInitParams = settings.FBInitParams; + model.Version = settings.Version; + model.SdkJs = settings.SdkJs; + if (!string.IsNullOrWhiteSpace(settings.AppSecret)) { - var protector = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core); - - model.AppId = settings.AppId; - model.FBInit = settings.FBInit; - model.FBInitParams = settings.FBInitParams; - model.Version = settings.Version; - model.SdkJs = settings.SdkJs; - if (!string.IsNullOrWhiteSpace(settings.AppSecret)) + try { - try - { - model.AppSecret = protector.Unprotect(settings.AppSecret); - } - catch (CryptographicException) - { - _logger.LogError("The app secret could not be decrypted. It may have been encrypted using a different key."); - model.AppSecret = string.Empty; - model.HasDecryptionError = true; - } + model.AppSecret = protector.Unprotect(settings.AppSecret); + } + catch (CryptographicException) + { + _logger.LogError("The app secret could not be decrypted. It may have been encrypted using a different key."); + model.AppSecret = string.Empty; + model.HasDecryptionError = true; } - }).Location("Content:0") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, FacebookSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) - { - return null; } + }).Location("Content:0") + .OnGroup(SettingsGroupId); + } - var model = new FacebookSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ISite site, FacebookSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - settings.AppId = model.AppId; - settings.FBInit = model.FBInit; - settings.SdkJs = model.SdkJs; - settings.Version = model.Version; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) + { + return null; + } - if (!string.IsNullOrWhiteSpace(model.FBInitParams)) - { - settings.FBInitParams = model.FBInitParams; - } + var model = new FacebookSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (context.Updater.ModelState.IsValid) - { - var protector = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core); - settings.AppSecret = protector.Protect(model.AppSecret); - } + settings.AppId = model.AppId; + settings.FBInit = model.FBInit; + settings.SdkJs = model.SdkJs; + settings.Version = model.Version; - _shellReleaseManager.RequestRelease(); + if (!string.IsNullOrWhiteSpace(model.FBInitParams)) + { + settings.FBInitParams = model.FBInitParams; + } - return await EditAsync(site, settings, context); + if (context.Updater.ModelState.IsValid) + { + var protector = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core); + settings.AppSecret = protector.Protect(model.AppSecret); } + + _shellReleaseManager.RequestRelease(); + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Extensions/OrchardCoreBuilderExtensions.cs index dab0ce5413a..4c11e287e4c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,20 +2,19 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Facebook.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureFacebookSettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureFacebookSettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Facebook"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Facebook"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Filters/FBInitFilter.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Filters/FBInitFilter.cs index 1bd37200406..a1ca8818b92 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Filters/FBInitFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Filters/FBInitFilter.cs @@ -5,38 +5,37 @@ using OrchardCore.ResourceManagement; using OrchardCore.Settings; -namespace OrchardCore.Facebook.Filters +namespace OrchardCore.Facebook.Filters; + +public sealed class FBInitFilter : IAsyncResultFilter { - public sealed class FBInitFilter : IAsyncResultFilter - { - private readonly IResourceManager _resourceManager; - private readonly ISiteService _siteService; + private readonly IResourceManager _resourceManager; + private readonly ISiteService _siteService; - public FBInitFilter( - IResourceManager resourceManager, - ISiteService siteService) - { - _resourceManager = resourceManager; - _siteService = siteService; - } + public FBInitFilter( + IResourceManager resourceManager, + ISiteService siteService) + { + _resourceManager = resourceManager; + _siteService = siteService; + } - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // Should only run on the front-end for a full view + if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) { - // Should only run on the front-end for a full view - if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) + var site = (await _siteService.GetSiteSettingsAsync()); + var settings = site.As(); + if (!string.IsNullOrWhiteSpace(settings?.AppId)) { - var site = (await _siteService.GetSiteSettingsAsync()); - var settings = site.As(); - if (!string.IsNullOrWhiteSpace(settings?.AppId)) + if (settings.FBInit) { - if (settings.FBInit) - { - var setting = _resourceManager.RegisterResource("script", "fb"); - setting.AtLocation(ResourceLocation.Foot); - } + var setting = _resourceManager.RegisterResource("script", "fb"); + setting.AtLocation(ResourceLocation.Foot); } } - await next.Invoke(); } + await next.Invoke(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Configuration/FacebookLoginConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Configuration/FacebookLoginConfiguration.cs index b70d3048814..ee284e7f208 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Configuration/FacebookLoginConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Configuration/FacebookLoginConfiguration.cs @@ -12,108 +12,107 @@ using OrchardCore.Facebook.Login.Settings; using OrchardCore.Facebook.Settings; -namespace OrchardCore.Facebook.Login.Configuration +namespace OrchardCore.Facebook.Login.Configuration; + +public class FacebookLoginConfiguration : + IConfigureOptions, + IConfigureNamedOptions { - public class FacebookLoginConfiguration : - IConfigureOptions, - IConfigureNamedOptions + private readonly FacebookSettings _facebookSettings; + private readonly IFacebookLoginService _loginService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ILogger _logger; + + public FacebookLoginConfiguration( + IOptions facebookSettings, + IFacebookLoginService loginService, + IDataProtectionProvider dataProtectionProvider, + ILogger logger) + { + _facebookSettings = facebookSettings.Value; + _loginService = loginService; + _dataProtectionProvider = dataProtectionProvider; + _logger = logger; + } + + public void Configure(AuthenticationOptions options) + { + if (_facebookSettings == null) + { + return; + } + + if (string.IsNullOrWhiteSpace(_facebookSettings.AppId) || string.IsNullOrWhiteSpace(_facebookSettings.AppSecret)) + { + _logger.LogWarning("The Facebook login provider is enabled but not configured."); + + return; + } + + var loginSettings = GetFacebookLoginSettingsAsync().GetAwaiter().GetResult(); + if (loginSettings == null) + { + return; + } + + // Register the OpenID Connect client handler in the authentication handlers collection. + options.AddScheme(FacebookDefaults.AuthenticationScheme, builder => + { + builder.DisplayName = "Facebook"; + builder.HandlerType = typeof(FacebookHandler); + }); + } + + public void Configure(string name, FacebookOptions options) { - private readonly FacebookSettings _facebookSettings; - private readonly IFacebookLoginService _loginService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly ILogger _logger; - - public FacebookLoginConfiguration( - IOptions facebookSettings, - IFacebookLoginService loginService, - IDataProtectionProvider dataProtectionProvider, - ILogger logger) + // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. + if (!string.Equals(name, FacebookDefaults.AuthenticationScheme, StringComparison.Ordinal)) { - _facebookSettings = facebookSettings.Value; - _loginService = loginService; - _dataProtectionProvider = dataProtectionProvider; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) + if (_facebookSettings == null) { - if (_facebookSettings == null) - { - return; - } - - if (string.IsNullOrWhiteSpace(_facebookSettings.AppId) || string.IsNullOrWhiteSpace(_facebookSettings.AppSecret)) - { - _logger.LogWarning("The Facebook login provider is enabled but not configured."); - - return; - } - - var loginSettings = GetFacebookLoginSettingsAsync().GetAwaiter().GetResult(); - if (loginSettings == null) - { - return; - } - - // Register the OpenID Connect client handler in the authentication handlers collection. - options.AddScheme(FacebookDefaults.AuthenticationScheme, builder => - { - builder.DisplayName = "Facebook"; - builder.HandlerType = typeof(FacebookHandler); - }); + return; } - public void Configure(string name, FacebookOptions options) + var loginSettings = GetFacebookLoginSettingsAsync().GetAwaiter().GetResult(); + if (loginSettings == null) { - // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. - if (!string.Equals(name, FacebookDefaults.AuthenticationScheme, StringComparison.Ordinal)) - { - return; - } - - if (_facebookSettings == null) - { - return; - } - - var loginSettings = GetFacebookLoginSettingsAsync().GetAwaiter().GetResult(); - if (loginSettings == null) - { - return; - } - - options.AppId = _facebookSettings.AppId; - - try - { - options.AppSecret = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core).Unprotect(_facebookSettings.AppSecret); - } - catch - { - _logger.LogError("The Facebook secret key could not be decrypted. It may have been encrypted using a different key."); - } - - if (loginSettings.CallbackPath.HasValue) - { - options.CallbackPath = loginSettings.CallbackPath; - } - - options.SaveTokens = loginSettings.SaveTokens; + return; } - public void Configure(FacebookOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.AppId = _facebookSettings.AppId; - private async Task GetFacebookLoginSettingsAsync() + try + { + options.AppSecret = _dataProtectionProvider.CreateProtector(FacebookConstants.Features.Core).Unprotect(_facebookSettings.AppSecret); + } + catch { - var settings = await _loginService.GetSettingsAsync(); - if ((await _loginService.ValidateSettingsAsync(settings)).Any(result => result != ValidationResult.Success)) - { - _logger.LogWarning("The Facebook Login module is not correctly configured."); + _logger.LogError("The Facebook secret key could not be decrypted. It may have been encrypted using a different key."); + } - return null; - } + if (loginSettings.CallbackPath.HasValue) + { + options.CallbackPath = loginSettings.CallbackPath; + } - return settings; + options.SaveTokens = loginSettings.SaveTokens; + } + + public void Configure(FacebookOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + + private async Task GetFacebookLoginSettingsAsync() + { + var settings = await _loginService.GetSettingsAsync(); + if ((await _loginService.ValidateSettingsAsync(settings)).Any(result => result != ValidationResult.Success)) + { + _logger.LogWarning("The Facebook Login module is not correctly configured."); + + return null; } + + return settings; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Drivers/FacebookLoginSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Drivers/FacebookLoginSettingsDisplayDriver.cs index 840eed947fb..79414cb76b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Drivers/FacebookLoginSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Drivers/FacebookLoginSettingsDisplayDriver.cs @@ -9,60 +9,59 @@ using OrchardCore.Facebook.Login.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Facebook.Login.Drivers +namespace OrchardCore.Facebook.Login.Drivers; + +public sealed class FacebookLoginSettingsDisplayDriver : SiteDisplayDriver { - public sealed class FacebookLoginSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + public FacebookLoginSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } + protected override string SettingsGroupId + => FacebookConstants.Features.Login; - public FacebookLoginSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ISite site, FacebookLoginSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; + return null; } - protected override string SettingsGroupId - => FacebookConstants.Features.Login; - public override async Task EditAsync(ISite site, FacebookLoginSettings settings, BuildEditorContext context) + return Initialize("FacebookLoginSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) - { - return null; - } - - return Initialize("FacebookLoginSettings_Edit", model => - { - model.CallbackPath = settings.CallbackPath.Value; - model.SaveTokens = settings.SaveTokens; - }).Location("Content:5") - .OnGroup(SettingsGroupId); - } + model.CallbackPath = settings.CallbackPath.Value; + model.SaveTokens = settings.SaveTokens; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - public override async Task UpdateAsync(ISite site, FacebookLoginSettings settings, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, FacebookLoginSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageFacebookApp)) - { - return null; - } + return null; + } - var model = new FacebookLoginSettingsViewModel(); + var model = new FacebookLoginSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.CallbackPath = model.CallbackPath; - settings.SaveTokens = model.SaveTokens; + settings.CallbackPath = model.CallbackPath; + settings.SaveTokens = model.SaveTokens; - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Recipes/FacebookLoginSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Recipes/FacebookLoginSettingsStep.cs index 7bdbdd6a5a3..733fb04b2c5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Recipes/FacebookLoginSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Recipes/FacebookLoginSettingsStep.cs @@ -6,38 +6,37 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Facebook.Login.Recipes +namespace OrchardCore.Facebook.Login.Recipes; + +/// +/// This recipe step sets general Facebook Login settings. +/// +public sealed class FacebookLoginSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets general Facebook Login settings. - /// - public sealed class FacebookLoginSettingsStep : IRecipeStepHandler + private readonly IFacebookLoginService _loginService; + + public FacebookLoginSettingsStep(IFacebookLoginService loginService) { - private readonly IFacebookLoginService _loginService; + _loginService = loginService; + } - public FacebookLoginSettingsStep(IFacebookLoginService loginService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(FacebookLoginSettings), StringComparison.OrdinalIgnoreCase)) { - _loginService = loginService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(FacebookLoginSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _loginService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _loginService.LoadSettingsAsync(); - settings.CallbackPath = model.CallbackPath; + settings.CallbackPath = model.CallbackPath; - await _loginService.UpdateSettingsAsync(settings); - } + await _loginService.UpdateSettingsAsync(settings); } +} - public sealed class FacebookLoginSettingsStepModel - { - public string CallbackPath { get; set; } - } +public sealed class FacebookLoginSettingsStepModel +{ + public string CallbackPath { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/FacebookLoginService.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/FacebookLoginService.cs index 19ae818b849..6f11da2ab35 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/FacebookLoginService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/FacebookLoginService.cs @@ -8,41 +8,40 @@ using OrchardCore.Facebook.Login.Settings; using OrchardCore.Settings; -namespace OrchardCore.Facebook.Login.Services +namespace OrchardCore.Facebook.Login.Services; + +public class FacebookLoginService : IFacebookLoginService { - public class FacebookLoginService : IFacebookLoginService + private readonly ISiteService _siteService; + + public FacebookLoginService(ISiteService siteService) + { + _siteService = siteService; + } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); + + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } + + public async Task UpdateSettingsAsync(FacebookLoginSettings settings) { - private readonly ISiteService _siteService; - - public FacebookLoginService(ISiteService siteService) - { - _siteService = siteService; - } - - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); - - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } - - public async Task UpdateSettingsAsync(FacebookLoginSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); - - var container = await _siteService.LoadSiteSettingsAsync(); - container.Properties[nameof(FacebookLoginSettings)] = JObject.FromObject(settings, JOptions.Default); - await _siteService.UpdateSiteSettingsAsync(container); - } - - public Task> ValidateSettingsAsync(FacebookLoginSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); - - var results = ImmutableArray.CreateBuilder(); - return Task.FromResult>(results); - } + ArgumentNullException.ThrowIfNull(settings); + + var container = await _siteService.LoadSiteSettingsAsync(); + container.Properties[nameof(FacebookLoginSettings)] = JObject.FromObject(settings, JOptions.Default); + await _siteService.UpdateSiteSettingsAsync(container); + } + + public Task> ValidateSettingsAsync(FacebookLoginSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); + + var results = ImmutableArray.CreateBuilder(); + return Task.FromResult>(results); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/IFacebookLoginService.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/IFacebookLoginService.cs index 90cc2667336..822cdfb014a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/IFacebookLoginService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Services/IFacebookLoginService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.Facebook.Login.Settings; -namespace OrchardCore.Facebook.Login.Services +namespace OrchardCore.Facebook.Login.Services; + +public interface IFacebookLoginService { - public interface IFacebookLoginService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(FacebookLoginSettings settings); - Task> ValidateSettingsAsync(FacebookLoginSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(FacebookLoginSettings settings); + Task> ValidateSettingsAsync(FacebookLoginSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Settings/FacebookLoginSettings.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Settings/FacebookLoginSettings.cs index fa63c059d24..564f2d54369 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Settings/FacebookLoginSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/Settings/FacebookLoginSettings.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Facebook.Login.Settings +namespace OrchardCore.Facebook.Login.Settings; + +public class FacebookLoginSettings { - public class FacebookLoginSettings - { - public PathString CallbackPath { get; set; } + public PathString CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/ViewModels/FacebookLoginSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/ViewModels/FacebookLoginSettingsViewModel.cs index ddee339501d..16a9645bc8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Login/ViewModels/FacebookLoginSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Login/ViewModels/FacebookLoginSettingsViewModel.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Facebook.Login.ViewModels +namespace OrchardCore.Facebook.Login.ViewModels; + +public class FacebookLoginSettingsViewModel { - public class FacebookLoginSettingsViewModel - { - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Recipes/FacebookSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Recipes/FacebookSettingsStep.cs index 53d5bf3eb8a..cd971562b31 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Recipes/FacebookSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Recipes/FacebookSettingsStep.cs @@ -5,48 +5,47 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Facebook.Recipes +namespace OrchardCore.Facebook.Recipes; + +/// +/// This recipe step sets general Facebook Login settings. +/// +public sealed class FacebookSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets general Facebook Login settings. - /// - public sealed class FacebookSettingsStep : IRecipeStepHandler + private readonly IFacebookService _facebookService; + + public FacebookSettingsStep(IFacebookService facebookService) { - private readonly IFacebookService _facebookService; + _facebookService = facebookService; + } - public FacebookSettingsStep(IFacebookService facebookService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "FacebookCoreSettings", StringComparison.OrdinalIgnoreCase)) { - _facebookService = facebookService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "FacebookCoreSettings", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _facebookService.GetSettingsAsync(); - - settings.AppId = model.AppId; - settings.AppSecret = model.AppSecret; - settings.SdkJs = model.SdkJs ?? "sdk.js"; - settings.FBInit = model.FBInit; - settings.FBInitParams = model.FBInitParams; - settings.Version = model.Version ?? "3.2"; - - await _facebookService.UpdateSettingsAsync(settings); - } - } + var model = context.Step.ToObject(); + var settings = await _facebookService.GetSettingsAsync(); - public sealed class FacebookCoreSettingsStepModel - { - public string AppId { get; set; } - public string AppSecret { get; set; } - public string SdkJs { get; set; } - public bool FBInit { get; set; } - public string FBInitParams { get; set; } - public string Version { get; set; } + settings.AppId = model.AppId; + settings.AppSecret = model.AppSecret; + settings.SdkJs = model.SdkJs ?? "sdk.js"; + settings.FBInit = model.FBInit; + settings.FBInitParams = model.FBInitParams; + settings.Version = model.Version ?? "3.2"; + + await _facebookService.UpdateSettingsAsync(settings); } } + +public sealed class FacebookCoreSettingsStepModel +{ + public string AppId { get; set; } + public string AppSecret { get; set; } + public string SdkJs { get; set; } + public bool FBInit { get; set; } + public string FBInitParams { get; set; } + public string Version { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/ResourceManagementOptionsConfiguration.cs index 38746d3cf71..beecba19c8d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/ResourceManagementOptionsConfiguration.cs @@ -1,26 +1,25 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.Facebook -{ - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions - { - private static readonly ResourceManifest _manifest; +namespace OrchardCore.Facebook; - static ResourceManagementOptionsConfiguration() - { - _manifest = new ResourceManifest(); +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions +{ + private static readonly ResourceManifest _manifest; - _manifest - .DefineScript("fb") - .SetDependencies("fbsdk") - .SetUrl("~/OrchardCore.Facebook/sdk/fb.js"); + static ResourceManagementOptionsConfiguration() + { + _manifest = new ResourceManifest(); - _manifest - .DefineScript("fbsdk") - .SetUrl("~/OrchardCore.Facebook/sdk/fbsdk.js"); - } + _manifest + .DefineScript("fb") + .SetDependencies("fbsdk") + .SetUrl("~/OrchardCore.Facebook/sdk/fb.js"); - public void Configure(ResourceManagementOptions options) => options.ResourceManifests.Add(_manifest); + _manifest + .DefineScript("fbsdk") + .SetUrl("~/OrchardCore.Facebook/sdk/fbsdk.js"); } + + public void Configure(ResourceManagementOptions options) => options.ResourceManifests.Add(_manifest); } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs index 32ee4c731b0..6fefeb8c52a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/ScriptsMiddleware.cs @@ -7,61 +7,60 @@ using OrchardCore.Facebook.Settings; using OrchardCore.Settings; -namespace OrchardCore.Facebook +namespace OrchardCore.Facebook; + +public class ScriptsMiddleware { - public class ScriptsMiddleware + private readonly RequestDelegate _next; + private readonly ISiteService _siteService; + + public ScriptsMiddleware(RequestDelegate next, ISiteService siteService) { - private readonly RequestDelegate _next; - private readonly ISiteService _siteService; + _next = next; + _siteService = siteService; + } - public ScriptsMiddleware(RequestDelegate next, ISiteService siteService) - { - _next = next; - _siteService = siteService; - } + public async Task Invoke(HttpContext httpContext) + { + ArgumentNullException.ThrowIfNull(httpContext); - public async Task Invoke(HttpContext httpContext) + if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Facebook/sdk", StringComparison.OrdinalIgnoreCase)) { - ArgumentNullException.ThrowIfNull(httpContext); + var script = default(string); + var settings = await _siteService.GetSettingsAsync(); - if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Facebook/sdk", StringComparison.OrdinalIgnoreCase)) + if (Path.GetFileName(httpContext.Request.Path.Value) == "fbsdk.js") { - var script = default(string); - var settings = await _siteService.GetSettingsAsync(); - - if (Path.GetFileName(httpContext.Request.Path.Value) == "fbsdk.js") - { - var locale = CultureInfo.CurrentUICulture.Name.Replace('-', '_'); - script = $@"(function(d){{ + var locale = CultureInfo.CurrentUICulture.Name.Replace('-', '_'); + script = $@"(function(d){{ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {{ return; }} js = d.createElement('script'); js.id = id; js.async = true; js.src = ""https://connect.facebook.net/{locale}/{settings.SdkJs}""; d.getElementsByTagName('head')[0].appendChild(js); }} (document));"; - } - else if (Path.GetFileName(httpContext.Request.Path.Value) == "fb.js") + } + else if (Path.GetFileName(httpContext.Request.Path.Value) == "fb.js") + { + if (!string.IsNullOrWhiteSpace(settings?.AppId)) { - if (!string.IsNullOrWhiteSpace(settings?.AppId)) - { - var options = $"{{ appId:'{settings.AppId}',version:'{settings.Version}'"; - options = string.IsNullOrWhiteSpace(settings.FBInitParams) - ? string.Concat(options, "}") - : string.Concat(options, ",", settings.FBInitParams, "}"); + var options = $"{{ appId:'{settings.AppId}',version:'{settings.Version}'"; + options = string.IsNullOrWhiteSpace(settings.FBInitParams) + ? string.Concat(options, "}") + : string.Concat(options, ",", settings.FBInitParams, "}"); - script = $"window.fbAsyncInit = function(){{ FB.init({options});}};"; - } + script = $"window.fbAsyncInit = function(){{ FB.init({options});}};"; } + } - if (script != null) - { - var bytes = Encoding.UTF8.GetBytes(script); - await httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(script).AsMemory(0, bytes.Length), httpContext.RequestAborted); + if (script != null) + { + var bytes = Encoding.UTF8.GetBytes(script); + await httpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(script).AsMemory(0, bytes.Length), httpContext.RequestAborted); - return; - } + return; } - - await _next.Invoke(httpContext); } + + await _next.Invoke(httpContext); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Services/FacebookService.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Services/FacebookService.cs index 2cf5856c730..7d48ffe51e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Services/FacebookService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Services/FacebookService.cs @@ -7,57 +7,56 @@ using OrchardCore.Facebook.Settings; using OrchardCore.Settings; -namespace OrchardCore.Facebook.Services +namespace OrchardCore.Facebook.Services; + +public class FacebookService : IFacebookService { - public class FacebookService : IFacebookService - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + protected readonly IStringLocalizer S; - public FacebookService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public FacebookService( + ISiteService siteService, + IStringLocalizer stringLocalizer) + { + _siteService = siteService; + S = stringLocalizer; + } - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); - public async Task UpdateSettingsAsync(FacebookSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + public async Task UpdateSettingsAsync(FacebookSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - var container = await _siteService.LoadSiteSettingsAsync(); - container.Properties[nameof(FacebookSettings)] = JObject.FromObject(settings); - await _siteService.UpdateSiteSettingsAsync(container); - } + var container = await _siteService.LoadSiteSettingsAsync(); + container.Properties[nameof(FacebookSettings)] = JObject.FromObject(settings); + await _siteService.UpdateSiteSettingsAsync(container); + } - public IEnumerable ValidateSettings(FacebookSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + public IEnumerable ValidateSettings(FacebookSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - var results = new List(); + var results = new List(); - if (string.IsNullOrEmpty(settings.AppId)) + if (string.IsNullOrEmpty(settings.AppId)) + { + results.Add(new ValidationResult(S["The AppId is required."], new[] { - results.Add(new ValidationResult(S["The AppId is required."], new[] - { - nameof(settings.AppId), - })); - } + nameof(settings.AppId), + })); + } - if (string.IsNullOrEmpty(settings.AppSecret)) + if (string.IsNullOrEmpty(settings.AppSecret)) + { + results.Add(new ValidationResult(S["The App Secret is required."], new[] { - results.Add(new ValidationResult(S["The App Secret is required."], new[] - { - nameof(settings.AppSecret), - })); - } - - return results; + nameof(settings.AppSecret), + })); } + + return results; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Services/IFacebookService.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Services/IFacebookService.cs index d650c2cd87a..446fe847247 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Services/IFacebookService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Services/IFacebookService.cs @@ -3,14 +3,13 @@ using System.Threading.Tasks; using OrchardCore.Facebook.Settings; -namespace OrchardCore.Facebook.Services +namespace OrchardCore.Facebook.Services; + +public interface IFacebookService { - public interface IFacebookService - { - Task GetSettingsAsync(); + Task GetSettingsAsync(); - Task UpdateSettingsAsync(FacebookSettings settings); + Task UpdateSettingsAsync(FacebookSettings settings); - IEnumerable ValidateSettings(FacebookSettings settings); - } + IEnumerable ValidateSettings(FacebookSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Settings/FacebookSettings.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Settings/FacebookSettings.cs index 6a83da487c3..af104d06ff2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Settings/FacebookSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Settings/FacebookSettings.cs @@ -1,16 +1,15 @@ -namespace OrchardCore.Facebook.Settings +namespace OrchardCore.Facebook.Settings; + +public class FacebookSettings { - public class FacebookSettings - { - public string AppId { get; set; } - public string AppSecret { get; set; } - public bool FBInit { get; set; } + public string AppId { get; set; } + public string AppSecret { get; set; } + public bool FBInit { get; set; } - public string FBInitParams { get; set; } = @"status:true, + public string FBInitParams { get; set; } = @"status:true, xfbml:true, autoLogAppEvents:true"; - public string SdkJs { get; set; } = "sdk.js"; - public string Version { get; set; } = "v3.2"; - } + public string SdkJs { get; set; } = "sdk.js"; + public string Version { get; set; } = "v3.2"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs index 3da3e4beca7..81154a25815 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Startup.cs @@ -17,31 +17,30 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Facebook +namespace OrchardCore.Facebook; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - builder.UseMiddleware(); - } + builder.UseMiddleware(); + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); - services.AddSingleton(); - services.AddScoped, FacebookSettingsDisplayDriver>(); - services.AddRecipeExecutionStep(); + services.AddSingleton(); + services.AddScoped, FacebookSettingsDisplayDriver>(); + services.AddRecipeExecutionStep(); - services.AddTransient, ResourceManagementOptionsConfiguration>(); - services.AddTransient, FacebookSettingsConfiguration>(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); + services.AddTransient, FacebookSettingsConfiguration>(); - services.Configure((options) => - { - options.Filters.Add(); - }); - } + services.Configure((options) => + { + options.Filters.Add(); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/StartupLogin.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/StartupLogin.cs index 898fd8204e0..b86b4f5fd1a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/StartupLogin.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/StartupLogin.cs @@ -15,40 +15,39 @@ using OrchardCore.Recipes; using OrchardCore.Settings; -namespace OrchardCore.Facebook +namespace OrchardCore.Facebook; + +[Feature(FacebookConstants.Features.Login)] +public sealed class StartupLogin : StartupBase { - [Feature(FacebookConstants.Features.Login)] - public sealed class StartupLogin : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); + services.AddScoped(); - services.AddSingleton(); - services.AddScoped, FacebookLoginSettingsDisplayDriver>(); - services.AddRecipeExecutionStep(); + services.AddSingleton(); + services.AddScoped, FacebookLoginSettingsDisplayDriver>(); + services.AddRecipeExecutionStep(); - // Register the options initializers required by the Facebook handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers: - ServiceDescriptor.Transient, FacebookLoginConfiguration>(), - ServiceDescriptor.Transient, FacebookLoginConfiguration>(), + // Register the options initializers required by the Facebook handler. + services.TryAddEnumerable(new[] + { + // Orchard-specific initializers: + ServiceDescriptor.Transient, FacebookLoginConfiguration>(), + ServiceDescriptor.Transient, FacebookLoginConfiguration>(), - // Deployment + // Deployment - // Built-in initializers: - ServiceDescriptor.Transient, OAuthPostConfigureOptions>() - }); - } + // Built-in initializers: + ServiceDescriptor.Transient, OAuthPostConfigureOptions>() + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/StartupWidgets.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/StartupWidgets.cs index 7b80ed8eebb..edbcffa7096 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/StartupWidgets.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/StartupWidgets.cs @@ -12,21 +12,20 @@ using OrchardCore.Facebook.Widgets.Settings; using OrchardCore.Modules; -namespace OrchardCore.Facebook +namespace OrchardCore.Facebook; + +[Feature(FacebookConstants.Features.Widgets)] +public sealed class StartupWidgets : StartupBase { - [Feature(FacebookConstants.Features.Widgets)] - public sealed class StartupWidgets : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/ErrorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/ErrorViewModel.cs index c111f9a74f7..98bcd7c33eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/ErrorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/ErrorViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Facebook.ViewModels +namespace OrchardCore.Facebook.ViewModels; + +public class ErrorViewModel { - public class ErrorViewModel - { - public string Error { get; set; } + public string Error { get; set; } - public string ErrorDescription { get; set; } - } + public string ErrorDescription { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/FacebookSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/FacebookSettingsViewModel.cs index 82d054e3394..5e1159ac329 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/FacebookSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/ViewModels/FacebookSettingsViewModel.cs @@ -1,24 +1,23 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Facebook.ViewModels +namespace OrchardCore.Facebook.ViewModels; + +public class FacebookSettingsViewModel { - public class FacebookSettingsViewModel - { - [Required] - public string AppId { get; set; } + [Required] + public string AppId { get; set; } - [Required] - public string AppSecret { get; set; } + [Required] + public string AppSecret { get; set; } - [Required] - public string SdkJs { get; set; } + [Required] + public string SdkJs { get; set; } - public bool FBInit { get; set; } - public string FBInitParams { get; set; } + public bool FBInit { get; set; } + public string FBInitParams { get; set; } - [RegularExpression(@"(v)\d+\.\d+")] - public string Version { get; set; } + [RegularExpression(@"(v)\d+\.\d+")] + public string Version { get; set; } - public bool HasDecryptionError { get; set; } - } + public bool HasDecryptionError { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Drivers/FacebookPluginPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Drivers/FacebookPluginPartDisplayDriver.cs index c0f34f78082..1ab621defee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Drivers/FacebookPluginPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Drivers/FacebookPluginPartDisplayDriver.cs @@ -11,81 +11,80 @@ using OrchardCore.Facebook.Widgets.ViewModels; using OrchardCore.Liquid; -namespace OrchardCore.Facebook.Widgets.Drivers -{ - public sealed class FacebookPluginPartDisplayDriver : ContentPartDisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ILiquidTemplateManager _liquidTemplateManager; +namespace OrchardCore.Facebook.Widgets.Drivers; - internal readonly IStringLocalizer S; +public sealed class FacebookPluginPartDisplayDriver : ContentPartDisplayDriver +{ + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ILiquidTemplateManager _liquidTemplateManager; - public FacebookPluginPartDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - ILiquidTemplateManager liquidTemplateManager, - IStringLocalizer localizer) - { - _contentDefinitionManager = contentDefinitionManager; - _liquidTemplateManager = liquidTemplateManager; - S = localizer; - } + internal readonly IStringLocalizer S; - public override IDisplayResult Display(FacebookPluginPart part, BuildPartDisplayContext context) - { - return Combine( - Initialize("FacebookPluginPart", async m => await BuildViewModelAsync(m, part)) - .Location("Detail", "Content"), - Initialize("FacebookPluginPart_Summary", async m => await BuildViewModelAsync(m, part)) - .Location("Summary", "Content") - ); - } + public FacebookPluginPartDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + ILiquidTemplateManager liquidTemplateManager, + IStringLocalizer localizer) + { + _contentDefinitionManager = contentDefinitionManager; + _liquidTemplateManager = liquidTemplateManager; + S = localizer; + } - private async Task BuildViewModelAsync(FacebookPluginPartViewModel model, FacebookPluginPart part) - { - ArgumentNullException.ThrowIfNull(model); - ArgumentNullException.ThrowIfNull(part); + public override IDisplayResult Display(FacebookPluginPart part, BuildPartDisplayContext context) + { + return Combine( + Initialize("FacebookPluginPart", async m => await BuildViewModelAsync(m, part)) + .Location("Detail", "Content"), + Initialize("FacebookPluginPart_Summary", async m => await BuildViewModelAsync(m, part)) + .Location("Summary", "Content") + ); + } - model.FacebookPluginPart = part; - model.Settings = await GetFacebookPluginPartSettingsAsync(part); - model.Liquid = part.Liquid; - model.ContentItem = part.ContentItem; - } + private async Task BuildViewModelAsync(FacebookPluginPartViewModel model, FacebookPluginPart part) + { + ArgumentNullException.ThrowIfNull(model); + ArgumentNullException.ThrowIfNull(part); - public override IDisplayResult Edit(FacebookPluginPart part, BuildPartEditorContext context) - { - return Initialize("FacebookPluginPart_Edit", async model => - { - model.Settings = await GetFacebookPluginPartSettingsAsync(part); - model.FacebookPluginPart = part; - model.Liquid = string.IsNullOrWhiteSpace(part.Liquid) ? model.Settings.Liquid : part.Liquid; - }); - } + model.FacebookPluginPart = part; + model.Settings = await GetFacebookPluginPartSettingsAsync(part); + model.Liquid = part.Liquid; + model.ContentItem = part.ContentItem; + } - private async Task GetFacebookPluginPartSettingsAsync(FacebookPluginPart part) + public override IDisplayResult Edit(FacebookPluginPart part, BuildPartEditorContext context) + { + return Initialize("FacebookPluginPart_Edit", async model => { - ArgumentNullException.ThrowIfNull(part); + model.Settings = await GetFacebookPluginPartSettingsAsync(part); + model.FacebookPluginPart = part; + model.Liquid = string.IsNullOrWhiteSpace(part.Liquid) ? model.Settings.Liquid : part.Liquid; + }); + } - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(FacebookPluginPart), StringComparison.Ordinal)); - return contentTypePartDefinition.GetSettings(); - } + private async Task GetFacebookPluginPartSettingsAsync(FacebookPluginPart part) + { + ArgumentNullException.ThrowIfNull(part); - public override async Task UpdateAsync(FacebookPluginPart model, UpdatePartEditorContext context) - { - var viewModel = new FacebookPluginPartViewModel(); + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, nameof(FacebookPluginPart), StringComparison.Ordinal)); + return contentTypePartDefinition.GetSettings(); + } - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Liquid); + public override async Task UpdateAsync(FacebookPluginPart model, UpdatePartEditorContext context) + { + var viewModel = new FacebookPluginPartViewModel(); - if (!string.IsNullOrEmpty(viewModel.Liquid) && !_liquidTemplateManager.Validate(viewModel.Liquid, out var errors)) - { - context.Updater.ModelState.AddModelError(nameof(model.Liquid), S["The FaceBook Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else - { - model.Liquid = viewModel.Liquid; - } + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Liquid); - return Edit(model, context); + if (!string.IsNullOrEmpty(viewModel.Liquid) && !_liquidTemplateManager.Validate(viewModel.Liquid, out var errors)) + { + context.Updater.ModelState.AddModelError(nameof(model.Liquid), S["The FaceBook Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + else + { + model.Liquid = viewModel.Liquid; } + + return Edit(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Handlers/FacebookPluginPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Handlers/FacebookPluginPartHandler.cs index 56f7c120aa6..a225d8950b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Handlers/FacebookPluginPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Handlers/FacebookPluginPartHandler.cs @@ -9,42 +9,41 @@ using OrchardCore.Facebook.Widgets.ViewModels; using OrchardCore.Liquid; -namespace OrchardCore.Facebook.Widgets.Handlers +namespace OrchardCore.Facebook.Widgets.Handlers; + +public class FacebookPluginPartHandler : ContentPartHandler { - public class FacebookPluginPartHandler : ContentPartHandler - { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - public FacebookPluginPartHandler(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) - { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - } + public FacebookPluginPartHandler(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, FacebookPluginPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, FacebookPluginPart part) + { + return context.ForAsync(async bodyAspect => { - return context.ForAsync(async bodyAspect => + try { - try + var model = new FacebookPluginPartViewModel() { - var model = new FacebookPluginPartViewModel() - { - Liquid = part.Liquid, - FacebookPluginPart = part, - ContentItem = part.ContentItem - }; + Liquid = part.Liquid, + FacebookPluginPart = part, + ContentItem = part.ContentItem + }; - var result = await _liquidTemplateManager.RenderHtmlContentAsync(part.Liquid, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + var result = await _liquidTemplateManager.RenderHtmlContentAsync(part.Liquid, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - bodyAspect.Body = result; - } - catch - { - bodyAspect.Body = HtmlString.Empty; - } - }); - } + bodyAspect.Body = result; + } + catch + { + bodyAspect.Body = HtmlString.Empty; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Models/FacebookPluginPart.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Models/FacebookPluginPart.cs index 0043ee28733..0d5fe629d0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Models/FacebookPluginPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Models/FacebookPluginPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Facebook.Widgets.Models +namespace OrchardCore.Facebook.Widgets.Models; + +public class FacebookPluginPart : ContentPart { - public class FacebookPluginPart : ContentPart - { - public string Liquid { get; set; } - } + public string Liquid { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettings.cs index 39c42815b71..88ddf951b41 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Facebook.Widgets.Settings +namespace OrchardCore.Facebook.Widgets.Settings; + +public class FacebookPluginPartSettings { - public class FacebookPluginPartSettings - { - public string Liquid { get; set; } - } + public string Liquid { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsDisplayDriver.cs index 291f16a9396..362138d7d19 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsDisplayDriver.cs @@ -7,46 +7,45 @@ using OrchardCore.Facebook.Widgets.Models; using OrchardCore.Liquid; -namespace OrchardCore.Facebook.Widgets.Settings -{ - public sealed class FacebookPluginPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly ILiquidTemplateManager _templateManager; +namespace OrchardCore.Facebook.Widgets.Settings; - internal readonly IStringLocalizer S; +public sealed class FacebookPluginPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver +{ + private readonly ILiquidTemplateManager _templateManager; - public FacebookPluginPartSettingsDisplayDriver(ILiquidTemplateManager templateManager, IStringLocalizer localizer) - { - _templateManager = templateManager; - S = localizer; - } + internal readonly IStringLocalizer S; - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) - { - return Initialize("FacebookPluginPartSettings_Edit", model => - { - model.FacebookPluginPartSettings = contentTypePartDefinition.GetSettings(); - model.Liquid = model.FacebookPluginPartSettings.Liquid; - }).Location("Content"); - } + public FacebookPluginPartSettingsDisplayDriver(ILiquidTemplateManager templateManager, IStringLocalizer localizer) + { + _templateManager = templateManager; + S = localizer; + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("FacebookPluginPartSettings_Edit", model => { - var model = new FacebookPluginPartSettingsViewModel(); + model.FacebookPluginPartSettings = contentTypePartDefinition.GetSettings(); + model.Liquid = model.FacebookPluginPartSettings.Liquid; + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Liquid); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new FacebookPluginPartSettingsViewModel(); - if (!string.IsNullOrEmpty(model.Liquid) && !_templateManager.Validate(model.Liquid, out var errors)) - { - context.Updater.ModelState.AddModelError(nameof(model.Liquid), S["The Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else - { - context.Builder.WithSettings(new FacebookPluginPartSettings { Liquid = model.Liquid }); - } + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Liquid); - return Edit(contentTypePartDefinition, context); + if (!string.IsNullOrEmpty(model.Liquid) && !_templateManager.Validate(model.Liquid, out var errors)) + { + context.Updater.ModelState.AddModelError(nameof(model.Liquid), S["The Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + else + { + context.Builder.WithSettings(new FacebookPluginPartSettings { Liquid = model.Liquid }); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsViewModel.cs index e891f63a60b..99dab044296 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/Settings/FacebookPluginPartSettingsViewModel.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Facebook.Widgets.Settings +namespace OrchardCore.Facebook.Widgets.Settings; + +public class FacebookPluginPartSettingsViewModel { - public class FacebookPluginPartSettingsViewModel - { - public string Liquid { get; set; } + public string Liquid { get; set; } - [BindNever] - public FacebookPluginPartSettings FacebookPluginPartSettings { get; set; } - } + [BindNever] + public FacebookPluginPartSettings FacebookPluginPartSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/ViewModels/FacebookPluginPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/ViewModels/FacebookPluginPartViewModel.cs index 05bd75bec38..caff2f178eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/ViewModels/FacebookPluginPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/ViewModels/FacebookPluginPartViewModel.cs @@ -3,20 +3,19 @@ using OrchardCore.Facebook.Widgets.Models; using OrchardCore.Facebook.Widgets.Settings; -namespace OrchardCore.Facebook.Widgets.ViewModels +namespace OrchardCore.Facebook.Widgets.ViewModels; + +public class FacebookPluginPartViewModel { - public class FacebookPluginPartViewModel - { - public string Liquid { get; set; } - public string Html { get; set; } + public string Liquid { get; set; } + public string Html { get; set; } - [BindNever] - public FacebookPluginPartSettings Settings { get; set; } + [BindNever] + public FacebookPluginPartSettings Settings { get; set; } - [BindNever] - public FacebookPluginPart FacebookPluginPart { get; set; } + [BindNever] + public FacebookPluginPart FacebookPluginPart { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } - } + [BindNever] + public ContentItem ContentItem { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/WidgetMigrations.cs b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/WidgetMigrations.cs index 4a28408cd42..aefa79aa60d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/WidgetMigrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Facebook/Widgets/WidgetMigrations.cs @@ -6,30 +6,29 @@ using OrchardCore.Recipes; using OrchardCore.Recipes.Services; -namespace OrchardCore.Facebook.Widgets +namespace OrchardCore.Facebook.Widgets; + +public sealed class WidgetMigrations : DataMigration { - public sealed class WidgetMigrations : DataMigration - { - private readonly IRecipeMigrator _recipeMigrator; - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IRecipeMigrator _recipeMigrator; + private readonly IContentDefinitionManager _contentDefinitionManager; - public WidgetMigrations( - IRecipeMigrator recipeMigrator, - IContentDefinitionManager contentDefinitionManager) - { - _recipeMigrator = recipeMigrator; - _contentDefinitionManager = contentDefinitionManager; - } + public WidgetMigrations( + IRecipeMigrator recipeMigrator, + IContentDefinitionManager contentDefinitionManager) + { + _recipeMigrator = recipeMigrator; + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(FacebookPluginPart), builder => builder - .Attachable() - .WithDescription("Provides a Facebook plugin part to create Facebook social plugin widgets.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync(nameof(FacebookPluginPart), builder => builder + .Attachable() + .WithDescription("Provides a Facebook plugin part to create Facebook social plugin widgets.")); - await _recipeMigrator.ExecuteAsync($"Widgets/migration{RecipesConstants.RecipeExtension}", this); + await _recipeMigrator.ExecuteAsync($"Widgets/migration{RecipesConstants.RecipeExtension}", this); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Features/AdminMenu.cs index ab3199d5c63..bf78a8b5d5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/AdminMenu.cs @@ -3,41 +3,40 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Features +namespace OrchardCore.Features; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", FeaturesConstants.FeatureId }, - // Since features admin accepts tenant, always pass empty string to create valid link for current tenant. - { "tenant", string.Empty }, - }; + { "area", FeaturesConstants.FeatureId }, + // Since features admin accepts tenant, always pass empty string to create valid link for current tenant. + { "tenant", string.Empty }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Features"], S["Features"].PrefixPosition(), deployment => deployment - .Action("Features", "Admin", _routeValues) - .Permission(Permissions.ManageFeatures) - .LocalNav() - ) - ); - return Task.CompletedTask; } + + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Features"], S["Features"].PrefixPosition(), deployment => deployment + .Action("Features", "Admin", _routeValues) + .Permission(Permissions.ManageFeatures) + .LocalNav() + ) + ); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs index ff3b0053fa6..01912488e80 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Controllers/AdminController.cs @@ -16,230 +16,229 @@ using OrchardCore.Features.ViewModels; using OrchardCore.Routing; -namespace OrchardCore.Features.Controllers +namespace OrchardCore.Features.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IExtensionManager _extensionManager; + private readonly IShellFeaturesManager _shellFeaturesManager; + private readonly AdminOptions _adminOptions; + private readonly INotifier _notifier; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + IAuthorizationService authorizationService, + IShellHost shellHost, + ShellSettings shellSettings, + IExtensionManager extensionManager, + IShellFeaturesManager shellFeaturesManager, + IOptions adminOptions, + INotifier notifier, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) { - private readonly IAuthorizationService _authorizationService; - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IExtensionManager _extensionManager; - private readonly IShellFeaturesManager _shellFeaturesManager; - private readonly AdminOptions _adminOptions; - private readonly INotifier _notifier; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - IAuthorizationService authorizationService, - IShellHost shellHost, - ShellSettings shellSettings, - IExtensionManager extensionManager, - IShellFeaturesManager shellFeaturesManager, - IOptions adminOptions, - INotifier notifier, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) - { - _authorizationService = authorizationService; - _shellHost = shellHost; - _shellSettings = shellSettings; - _extensionManager = extensionManager; - _shellFeaturesManager = shellFeaturesManager; - _adminOptions = adminOptions.Value; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; + _authorizationService = authorizationService; + _shellHost = shellHost; + _shellSettings = shellSettings; + _extensionManager = extensionManager; + _shellFeaturesManager = shellFeaturesManager; + _adminOptions = adminOptions.Value; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + } + + [Admin("Features/{tenant?}", "Features")] + public async Task Features(string tenant) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); } - [Admin("Features/{tenant?}", "Features")] - public async Task Features(string tenant) + var viewModel = new FeaturesViewModel(); + + await ExecuteAsync(tenant, async (featureService, settings, isProxy) => { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + // If the user provide an invalid tenant value, we'll set it to null so it's not available on the next request. + if (isProxy) { - return Forbid(); + viewModel.IsProxy = true; + tenant = settings.Name; + viewModel.Name = settings.Name; } - var viewModel = new FeaturesViewModel(); + viewModel.Features = await featureService.GetModuleFeaturesAsync(); + }); - await ExecuteAsync(tenant, async (featureService, settings, isProxy) => - { - // If the user provide an invalid tenant value, we'll set it to null so it's not available on the next request. - if (isProxy) - { - viewModel.IsProxy = true; - tenant = settings.Name; - viewModel.Name = settings.Name; - } - - viewModel.Features = await featureService.GetModuleFeaturesAsync(); - }); + return View(viewModel); + } - return View(viewModel); + [HttpPost] + [FormValueRequired("submit.BulkAction")] + public async Task Features(BulkActionViewModel model, bool? force, string tenant) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); } - [HttpPost] - [FormValueRequired("submit.BulkAction")] - public async Task Features(BulkActionViewModel model, bool? force, string tenant) + if (model.FeatureIds == null || model.FeatureIds.Length == 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) - { - return Forbid(); - } + ModelState.AddModelError(nameof(BulkActionViewModel.FeatureIds), S["Please select one or more features."]); + } - if (model.FeatureIds == null || model.FeatureIds.Length == 0) + if (ModelState.IsValid) + { + await ExecuteAsync(tenant, async (featureService, settings, isProxy) => { - ModelState.AddModelError(nameof(BulkActionViewModel.FeatureIds), S["Please select one or more features."]); - } + var availableFeatures = await featureService.GetAvailableFeatures(model.FeatureIds); - if (ModelState.IsValid) - { - await ExecuteAsync(tenant, async (featureService, settings, isProxy) => - { - var availableFeatures = await featureService.GetAvailableFeatures(model.FeatureIds); + await featureService.EnableOrDisableFeaturesAsync(availableFeatures, model.BulkAction, force, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); + }); + } - await featureService.EnableOrDisableFeaturesAsync(availableFeatures, model.BulkAction, force, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); - }); - } + return RedirectToAction(nameof(Features)); + } - return RedirectToAction(nameof(Features)); + [HttpPost] + [Admin("Features/{id}/Disable/{tenant?}", "FeaturesDisable")] + public async Task Disable(string id, string tenant) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); } - [HttpPost] - [Admin("Features/{id}/Disable/{tenant?}", "FeaturesDisable")] - public async Task Disable(string id, string tenant) + var found = false; + + await ExecuteAsync(tenant, async (featureService, settings, isProxy) => { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + var feature = await featureService.GetAvailableFeature(id); + + if (feature == null) { - return Forbid(); + return; } - var found = false; + found = true; - await ExecuteAsync(tenant, async (featureService, settings, isProxy) => + if (!isProxy && id == FeaturesConstants.FeatureId) { - var feature = await featureService.GetAvailableFeature(id); - - if (feature == null) - { - return; - } + await _notifier.ErrorAsync(H["This feature is always enabled and cannot be disabled."]); - found = true; - - if (!isProxy && id == FeaturesConstants.FeatureId) - { - await _notifier.ErrorAsync(H["This feature is always enabled and cannot be disabled."]); + return; + } - return; - } + await featureService.EnableOrDisableFeaturesAsync(new[] { feature }, FeaturesBulkAction.Disable, true, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); + }); - await featureService.EnableOrDisableFeaturesAsync(new[] { feature }, FeaturesBulkAction.Disable, true, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); - }); + if (!found) + { + return NotFound(); + } - if (!found) - { - return NotFound(); - } + return Redirect(GetNextUrl(tenant, id)); + } - return Redirect(GetNextUrl(tenant, id)); + [HttpPost] + [Admin("Features/{id}/Enable/{tenant?}", "FeaturesEnable")] + public async Task Enable(string id, string tenant) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + { + return Forbid(); } - [HttpPost] - [Admin("Features/{id}/Enable/{tenant?}", "FeaturesEnable")] - public async Task Enable(string id, string tenant) + var found = false; + + await ExecuteAsync(tenant, async (featureService, settings, isProxy) => { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageFeatures)) + var feature = await featureService.GetAvailableFeature(id); + + if (feature == null) { - return Forbid(); + return; } - var found = false; + found = true; - await ExecuteAsync(tenant, async (featureService, settings, isProxy) => - { - var feature = await featureService.GetAvailableFeature(id); + await featureService.EnableOrDisableFeaturesAsync(new[] { feature }, FeaturesBulkAction.Enable, true, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); + }); - if (feature == null) - { - return; - } + if (!found) + { + return NotFound(); + } - found = true; + return Redirect(GetNextUrl(tenant, id)); + } - await featureService.EnableOrDisableFeaturesAsync(new[] { feature }, FeaturesBulkAction.Enable, true, async (features, isEnabled) => await NotifyAsync(features, isEnabled)); - }); + private async Task ExecuteAsync(string tenant, Func action) + { + if (_shellSettings.IsDefaultShell() && + !string.IsNullOrWhiteSpace(tenant) && + _shellHost.TryGetSettings(tenant, out var settings) && + !settings.IsDefaultShell() && + settings.IsRunning()) + { + // At this point, we know that this request is being executed from the Default tenant. + // Also, we were able to find a matching and running tenant that isn't the Default one. + // Therefore, it is safe to create a scope for the given tenant. + var shellScope = await _shellHost.GetScopeAsync(settings); - if (!found) + await shellScope.UsingAsync(async scope => { - return NotFound(); - } + var shellFeatureManager = scope.ServiceProvider.GetRequiredService(); + var extensionManager = scope.ServiceProvider.GetRequiredService(); - return Redirect(GetNextUrl(tenant, id)); - } + // At this point we apply the action on the given tenant. + await action(new FeatureService(shellFeatureManager, extensionManager), scope.ShellContext.Settings, true); + }); - private async Task ExecuteAsync(string tenant, Func action) - { - if (_shellSettings.IsDefaultShell() && - !string.IsNullOrWhiteSpace(tenant) && - _shellHost.TryGetSettings(tenant, out var settings) && - !settings.IsDefaultShell() && - settings.IsRunning()) - { - // At this point, we know that this request is being executed from the Default tenant. - // Also, we were able to find a matching and running tenant that isn't the Default one. - // Therefore, it is safe to create a scope for the given tenant. - var shellScope = await _shellHost.GetScopeAsync(settings); + return; + } - await shellScope.UsingAsync(async scope => - { - var shellFeatureManager = scope.ServiceProvider.GetRequiredService(); - var extensionManager = scope.ServiceProvider.GetRequiredService(); + // At this point we apply the action on the current tenant. + await action(new FeatureService(_shellFeaturesManager, _extensionManager), _shellSettings, false); + } - // At this point we apply the action on the given tenant. - await action(new FeatureService(shellFeatureManager, extensionManager), scope.ShellContext.Settings, true); - }); + private string GetNextUrl(string tenant, string featureId) + { + // Generating routes can fail while the tenant is recycled as routes can use services. + // It could be fixed by waiting for the next request or the end of the current one + // to actually release the tenant. Right now we render the url before recycling the tenant. - return; - } + ShellSettings settings = null; - // At this point we apply the action on the current tenant. - await action(new FeatureService(_shellFeaturesManager, _extensionManager), _shellSettings, false); + if (!string.IsNullOrWhiteSpace(tenant)) + { + _shellHost.TryGetSettings(tenant, out settings); } - private string GetNextUrl(string tenant, string featureId) + if (settings != null && settings.Name != _shellSettings.Name) { - // Generating routes can fail while the tenant is recycled as routes can use services. - // It could be fixed by waiting for the next request or the end of the current one - // to actually release the tenant. Right now we render the url before recycling the tenant. - - ShellSettings settings = null; - - if (!string.IsNullOrWhiteSpace(tenant)) - { - _shellHost.TryGetSettings(tenant, out settings); - } - - if (settings != null && settings.Name != _shellSettings.Name) - { - return Url.Action(nameof(Features), new { tenant }); - } - - if (!settings.IsDefaultShell() && featureId == FeaturesConstants.FeatureId) - { - return Url.Content("~/" + _adminOptions.AdminUrlPrefix); - } + return Url.Action(nameof(Features), new { tenant }); + } - return Url.Action(nameof(Features)); + if (!settings.IsDefaultShell() && featureId == FeaturesConstants.FeatureId) + { + return Url.Content("~/" + _adminOptions.AdminUrlPrefix); } - private async ValueTask NotifyAsync(IEnumerable features, bool enabled = true) + return Url.Action(nameof(Features)); + } + + private async ValueTask NotifyAsync(IEnumerable features, bool enabled = true) + { + foreach (var feature in features) { - foreach (var feature in features) - { - await _notifier.SuccessAsync(H["{0} was {1}.", feature.Name ?? feature.Id, enabled ? "enabled" : "disabled"]); - } + await _notifier.SuccessAsync(H["{0} was {1}.", feature.Name ?? feature.Id, enabled ? "enabled" : "disabled"]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentSource.cs index 04c038b74ca..77b88ad14da 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentSource.cs @@ -4,37 +4,36 @@ using OrchardCore.Deployment; using OrchardCore.Features.Services; -namespace OrchardCore.Features.Deployment +namespace OrchardCore.Features.Deployment; + +public class AllFeaturesDeploymentSource : IDeploymentSource { - public class AllFeaturesDeploymentSource : IDeploymentSource + private readonly IModuleService _moduleService; + + public AllFeaturesDeploymentSource(IModuleService moduleService) { - private readonly IModuleService _moduleService; + _moduleService = moduleService; + } - public AllFeaturesDeploymentSource(IModuleService moduleService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AllFeaturesDeploymentStep allFeaturesStep) { - _moduleService = moduleService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var features = await _moduleService.GetAvailableFeaturesAsync(); + var featureStep = new JsonObject { - if (step is not AllFeaturesDeploymentStep allFeaturesStep) - { - return; - } + ["name"] = "Feature", + ["enable"] = JNode.FromObject(features.Where(f => f.IsEnabled).Select(f => f.Descriptor.Id).ToArray()), + }; - var features = await _moduleService.GetAvailableFeaturesAsync(); - var featureStep = new JsonObject - { - ["name"] = "Feature", - ["enable"] = JNode.FromObject(features.Where(f => f.IsEnabled).Select(f => f.Descriptor.Id).ToArray()), - }; - - if (!allFeaturesStep.IgnoreDisabledFeatures) - { - featureStep.Add("disable", JNode.FromObject(features.Where(f => !f.IsEnabled).Select(f => f.Descriptor.Id).ToArray())); - } - - result.Steps.Add(featureStep); + if (!allFeaturesStep.IgnoreDisabledFeatures) + { + featureStep.Add("disable", JNode.FromObject(features.Where(f => !f.IsEnabled).Select(f => f.Descriptor.Id).ToArray())); } + + result.Steps.Add(featureStep); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStep.cs index f241626f3a6..5057b56c4bf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStep.cs @@ -1,17 +1,16 @@ using OrchardCore.Deployment; -namespace OrchardCore.Features.Deployment +namespace OrchardCore.Features.Deployment; + +/// +/// Adds enabled and disabled features to a . +/// +public class AllFeaturesDeploymentStep : DeploymentStep { - /// - /// Adds enabled and disabled features to a . - /// - public class AllFeaturesDeploymentStep : DeploymentStep + public AllFeaturesDeploymentStep() { - public AllFeaturesDeploymentStep() - { - Name = "AllFeatures"; - } - - public bool IgnoreDisabledFeatures { get; set; } + Name = "AllFeatures"; } + + public bool IgnoreDisabledFeatures { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStepDriver.cs index ea3af1ac92d..7774666cab2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Deployment/AllFeaturesDeploymentStepDriver.cs @@ -4,32 +4,31 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Features.ViewModels; -namespace OrchardCore.Features.Deployment +namespace OrchardCore.Features.Deployment; + +public sealed class AllFeaturesDeploymentStepDriver : DisplayDriver { - public sealed class AllFeaturesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllFeaturesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllFeaturesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllFeaturesDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("AllFeaturesDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllFeaturesDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("AllFeaturesDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllFeaturesDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(AllFeaturesDeploymentStep step, BuildEditorContext context) + { + return Initialize("AllFeaturesDeploymentStep_Fields_Edit", model => { - return Initialize("AllFeaturesDeploymentStep_Fields_Edit", model => - { - model.IgnoreDisabledFeatures = step.IgnoreDisabledFeatures; - }).Location("Content"); - } + model.IgnoreDisabledFeatures = step.IgnoreDisabledFeatures; + }).Location("Content"); + } - public override async Task UpdateAsync(AllFeaturesDeploymentStep step, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.IgnoreDisabledFeatures); + public override async Task UpdateAsync(AllFeaturesDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.IgnoreDisabledFeatures); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Models/ModuleEntry.cs b/src/OrchardCore.Modules/OrchardCore.Features/Models/ModuleEntry.cs index 4b0452bd170..979d086a305 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Models/ModuleEntry.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Models/ModuleEntry.cs @@ -1,44 +1,43 @@ using System.Collections.Generic; using OrchardCore.Environment.Extensions; -namespace OrchardCore.Features.Models +namespace OrchardCore.Features.Models; + +/// +/// Represents a module. +/// +public class ModuleEntry { /// - /// Represents a module. + /// Default constructor. /// - public class ModuleEntry + public ModuleEntry() { - /// - /// Default constructor. - /// - public ModuleEntry() - { - Notifications = []; - } + Notifications = []; + } - /// - /// The module's extension descriptor. - /// - public IExtensionInfo Descriptor { get; set; } + /// + /// The module's extension descriptor. + /// + public IExtensionInfo Descriptor { get; set; } - /// - /// Boolean value indicating if the module needs a version update. - /// - public bool NeedsVersionUpdate { get; set; } + /// + /// Boolean value indicating if the module needs a version update. + /// + public bool NeedsVersionUpdate { get; set; } - /// - /// Boolean value indicating if the feature was recently installed. - /// - public bool IsRecentlyInstalled { get; set; } + /// + /// Boolean value indicating if the feature was recently installed. + /// + public bool IsRecentlyInstalled { get; set; } - /// - /// List of module notifications. - /// - public List Notifications { get; set; } + /// + /// List of module notifications. + /// + public List Notifications { get; set; } - /// - /// Indicates whether the module can be uninstalled by the user. - /// - public bool CanUninstall { get; set; } - } + /// + /// Indicates whether the module can be uninstalled by the user. + /// + public bool CanUninstall { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Recipes/Executors/FeatureStep.cs b/src/OrchardCore.Modules/OrchardCore.Features/Recipes/Executors/FeatureStep.cs index 71531374ba5..75688816e08 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Recipes/Executors/FeatureStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Recipes/Executors/FeatureStep.cs @@ -6,45 +6,44 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Features.Recipes.Executors +namespace OrchardCore.Features.Recipes.Executors; + +/// +/// This recipe step enables or disables a set of features. +/// +public sealed class FeatureStep : IRecipeStepHandler { - /// - /// This recipe step enables or disables a set of features. - /// - public sealed class FeatureStep : IRecipeStepHandler + private readonly IShellFeaturesManager _shellFeaturesManager; + + public FeatureStep(IShellFeaturesManager shellFeaturesManager) { - private readonly IShellFeaturesManager _shellFeaturesManager; + _shellFeaturesManager = shellFeaturesManager; + } - public FeatureStep(IShellFeaturesManager shellFeaturesManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Feature", StringComparison.OrdinalIgnoreCase)) { - _shellFeaturesManager = shellFeaturesManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "Feature", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var step = context.Step.ToObject(); + var step = context.Step.ToObject(); - var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); + var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); - var featuresToDisable = features.Where(x => step.Disable?.Contains(x.Id) == true).ToArray(); - var featuresToEnable = features.Where(x => step.Enable?.Contains(x.Id) == true).ToArray(); + var featuresToDisable = features.Where(x => step.Disable?.Contains(x.Id) == true).ToArray(); + var featuresToEnable = features.Where(x => step.Enable?.Contains(x.Id) == true).ToArray(); - if (featuresToDisable.Length > 0 || featuresToEnable.Length > 0) - { - await _shellFeaturesManager.UpdateFeaturesAsync(featuresToDisable, featuresToEnable, true); - } - } - - private sealed class FeatureStepModel + if (featuresToDisable.Length > 0 || featuresToEnable.Length > 0) { - public string Name { get; set; } - public string[] Disable { get; set; } - public string[] Enable { get; set; } + await _shellFeaturesManager.UpdateFeaturesAsync(featuresToDisable, featuresToEnable, true); } } + + private sealed class FeatureStepModel + { + public string Name { get; set; } + public string[] Disable { get; set; } + public string[] Enable { get; set; } + } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Services/IModuleService.cs b/src/OrchardCore.Modules/OrchardCore.Features/Services/IModuleService.cs index b83c1adf73e..a93f11fc1eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Services/IModuleService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Services/IModuleService.cs @@ -2,40 +2,39 @@ using System.Threading.Tasks; using OrchardCore.Features.Models; -namespace OrchardCore.Features.Services +namespace OrchardCore.Features.Services; + +public interface IModuleService { - public interface IModuleService - { - /// - /// Retrieves an enumeration of the available features together with its state (enabled / disabled). - /// - /// An enumeration of the available features together with its state (enabled / disabled). - Task> GetAvailableFeaturesAsync(); + /// + /// Retrieves an enumeration of the available features together with its state (enabled / disabled). + /// + /// An enumeration of the available features together with its state (enabled / disabled). + Task> GetAvailableFeaturesAsync(); - /// - /// Enables a list of features. - /// - /// The IDs for the features to be enabled. - Task EnableFeaturesAsync(IEnumerable featureIds); + /// + /// Enables a list of features. + /// + /// The IDs for the features to be enabled. + Task EnableFeaturesAsync(IEnumerable featureIds); - /// - /// Enables a list of features. - /// - /// The IDs for the features to be enabled. - /// Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise. - Task EnableFeaturesAsync(IEnumerable featureIds, bool force); + /// + /// Enables a list of features. + /// + /// The IDs for the features to be enabled. + /// Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise. + Task EnableFeaturesAsync(IEnumerable featureIds, bool force); - /// - /// Disables a list of features. - /// - /// The IDs for the features to be disabled. - Task DisableFeaturesAsync(IEnumerable featureIds); + /// + /// Disables a list of features. + /// + /// The IDs for the features to be disabled. + Task DisableFeaturesAsync(IEnumerable featureIds); - /// - /// Disables a list of features. - /// - /// The IDs for the features to be disabled. - /// Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise. - Task DisableFeaturesAsync(IEnumerable featureIds, bool force); - } + /// + /// Disables a list of features. + /// + /// The IDs for the features to be disabled. + /// Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise. + Task DisableFeaturesAsync(IEnumerable featureIds, bool force); } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Services/ModuleService.cs b/src/OrchardCore.Modules/OrchardCore.Features/Services/ModuleService.cs index 051791852df..2d02c1c973f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Services/ModuleService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Services/ModuleService.cs @@ -8,156 +8,155 @@ using OrchardCore.Environment.Shell; using OrchardCore.Features.Models; -namespace OrchardCore.Features.Services +namespace OrchardCore.Features.Services; + +public class ModuleService : IModuleService { - public class ModuleService : IModuleService + private readonly IExtensionManager _extensionManager; + private readonly IShellFeaturesManager _shellFeaturesManager; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public ModuleService( + IExtensionManager extensionManager, + IShellFeaturesManager shellFeaturesManager, + IHtmlLocalizer htmlLocalizer, + INotifier notifier) { - private readonly IExtensionManager _extensionManager; - private readonly IShellFeaturesManager _shellFeaturesManager; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - - public ModuleService( - IExtensionManager extensionManager, - IShellFeaturesManager shellFeaturesManager, - IHtmlLocalizer htmlLocalizer, - INotifier notifier) - { - _notifier = notifier; - _extensionManager = extensionManager; - _shellFeaturesManager = shellFeaturesManager; - H = htmlLocalizer; - } + _notifier = notifier; + _extensionManager = extensionManager; + _shellFeaturesManager = shellFeaturesManager; + H = htmlLocalizer; + } - /// - /// Retrieves an enumeration of the available features together with its state (enabled / disabled). - /// - /// An enumeration of the available features together with its state (enabled / disabled). - public async Task> GetAvailableFeaturesAsync() - { - var enabledFeatures = - await _shellFeaturesManager.GetEnabledFeaturesAsync(); + /// + /// Retrieves an enumeration of the available features together with its state (enabled / disabled). + /// + /// An enumeration of the available features together with its state (enabled / disabled). + public async Task> GetAvailableFeaturesAsync() + { + var enabledFeatures = + await _shellFeaturesManager.GetEnabledFeaturesAsync(); - var availableFeatures = _extensionManager.GetFeatures(); + var availableFeatures = _extensionManager.GetFeatures(); - return availableFeatures - .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures - .Any(sf => sf.Id == f.Id))); - } + return availableFeatures + .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures + .Any(sf => sf.Id == f.Id))); + } - /// - /// Enables a list of features. - /// - /// The IDs for the features to be enabled. - public Task EnableFeaturesAsync(IEnumerable featureIds) - { - return EnableFeaturesAsync(featureIds, false); - } + /// + /// Enables a list of features. + /// + /// The IDs for the features to be enabled. + public Task EnableFeaturesAsync(IEnumerable featureIds) + { + return EnableFeaturesAsync(featureIds, false); + } - /// - /// Enables a list of features. - /// - /// The IDs for the features to be enabled. - /// Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise. - public async Task EnableFeaturesAsync(IEnumerable featureIds, bool force) - { - var featuresToEnable = _extensionManager - .GetFeatures() - .Where(x => featureIds.Contains(x.Id)); - - var enabledFeatures = await _shellFeaturesManager.EnableFeaturesAsync(featuresToEnable, force); - foreach (var enabledFeature in enabledFeatures) - { - await _notifier.SuccessAsync(H["{0} was enabled.", enabledFeature.Name]); - } - } + /// + /// Enables a list of features. + /// + /// The IDs for the features to be enabled. + /// Boolean parameter indicating if the feature should enable it's dependencies if required or fail otherwise. + public async Task EnableFeaturesAsync(IEnumerable featureIds, bool force) + { + var featuresToEnable = _extensionManager + .GetFeatures() + .Where(x => featureIds.Contains(x.Id)); - /// - /// Disables a list of features. - /// - /// The IDs for the features to be disabled. - public Task DisableFeaturesAsync(IEnumerable featureIds) + var enabledFeatures = await _shellFeaturesManager.EnableFeaturesAsync(featuresToEnable, force); + foreach (var enabledFeature in enabledFeatures) { - return DisableFeaturesAsync(featureIds, false); + await _notifier.SuccessAsync(H["{0} was enabled.", enabledFeature.Name]); } + } - /// - /// Disables a list of features. - /// - /// The IDs for the features to be disabled. - /// Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise. - public async Task DisableFeaturesAsync(IEnumerable featureIds, bool force) - { - var featuresToDisable = _extensionManager - .GetFeatures() - .Where(x => featureIds.Contains(x.Id)); - - var features = await _shellFeaturesManager.DisableFeaturesAsync(featuresToDisable, force); - foreach (var feature in features) - { - await _notifier.SuccessAsync(H["{0} was disabled.", feature.Name]); - } - } + /// + /// Disables a list of features. + /// + /// The IDs for the features to be disabled. + public Task DisableFeaturesAsync(IEnumerable featureIds) + { + return DisableFeaturesAsync(featureIds, false); + } - ///// - ///// Determines if a module was recently installed by using the project's last written time. - ///// - ///// The extension descriptor. - // public bool IsRecentlyInstalled(ExtensionDescriptor extensionDescriptor) { - // DateTime lastWrittenUtc = _cacheManager.Get(extensionDescriptor, descriptor => { - // string projectFile = GetManifestPath(extensionDescriptor); - // if (!string.IsNullOrEmpty(projectFile)) { - // // If project file was modified less than 24 hours ago, the module was recently deployed - // return _virtualPathProvider.GetFileLastWriteTimeUtc(projectFile); - // } - - // return DateTime.UtcNow; - // }); - - // return DateTime.UtcNow.Subtract(lastWrittenUtc) < new TimeSpan(1, 0, 0, 0); - // } - - ///// - ///// Retrieves the full path of the manifest file for a module's extension descriptor. - ///// - ///// The module's extension descriptor. - ///// The full path to the module's manifest file. - // private string GetManifestPath(ExtensionDescriptor extensionDescriptor) { - // string projectPath = _virtualPathProvider.Combine(extensionDescriptor.Location, extensionDescriptor.Id, "module.txt"); - - // if (!_virtualPathProvider.FileExists(projectPath)) { - // return null; - // } - - // return projectPath; - // } - - private static ModuleFeature AssembleModuleFromDescriptor(IFeatureInfo featureInfo, bool isEnabled) + /// + /// Disables a list of features. + /// + /// The IDs for the features to be disabled. + /// Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise. + public async Task DisableFeaturesAsync(IEnumerable featureIds, bool force) + { + var featuresToDisable = _extensionManager + .GetFeatures() + .Where(x => featureIds.Contains(x.Id)); + + var features = await _shellFeaturesManager.DisableFeaturesAsync(featuresToDisable, force); + foreach (var feature in features) { - return new ModuleFeature - { - Descriptor = featureInfo, - IsEnabled = isEnabled - }; + await _notifier.SuccessAsync(H["{0} was disabled.", feature.Name]); } + } - // private void GenerateWarning(string messageFormat, string featureName, IEnumerable featuresInQuestion) { - // if (featuresInQuestion.Count() < 1) - // return; - - // Services.Notifier.Warning(T( - // messageFormat, - // featureName, - // featuresInQuestion.Count() > 1 - // ? string.Join("", - // featuresInQuestion.Select( - // (fn, i) => - // T(i == featuresInQuestion.Count() - 1 - // ? "{0}" - // : (i == featuresInQuestion.Count() - 2 - // ? "{0} and " - // : "{0}, "), fn).ToString()).ToArray()) - // : featuresInQuestion.First())); - // } + ///// + ///// Determines if a module was recently installed by using the project's last written time. + ///// + ///// The extension descriptor. + // public bool IsRecentlyInstalled(ExtensionDescriptor extensionDescriptor) { + // DateTime lastWrittenUtc = _cacheManager.Get(extensionDescriptor, descriptor => { + // string projectFile = GetManifestPath(extensionDescriptor); + // if (!string.IsNullOrEmpty(projectFile)) { + // // If project file was modified less than 24 hours ago, the module was recently deployed + // return _virtualPathProvider.GetFileLastWriteTimeUtc(projectFile); + // } + + // return DateTime.UtcNow; + // }); + + // return DateTime.UtcNow.Subtract(lastWrittenUtc) < new TimeSpan(1, 0, 0, 0); + // } + + ///// + ///// Retrieves the full path of the manifest file for a module's extension descriptor. + ///// + ///// The module's extension descriptor. + ///// The full path to the module's manifest file. + // private string GetManifestPath(ExtensionDescriptor extensionDescriptor) { + // string projectPath = _virtualPathProvider.Combine(extensionDescriptor.Location, extensionDescriptor.Id, "module.txt"); + + // if (!_virtualPathProvider.FileExists(projectPath)) { + // return null; + // } + + // return projectPath; + // } + + private static ModuleFeature AssembleModuleFromDescriptor(IFeatureInfo featureInfo, bool isEnabled) + { + return new ModuleFeature + { + Descriptor = featureInfo, + IsEnabled = isEnabled + }; } + + // private void GenerateWarning(string messageFormat, string featureName, IEnumerable featuresInQuestion) { + // if (featuresInQuestion.Count() < 1) + // return; + + // Services.Notifier.Warning(T( + // messageFormat, + // featureName, + // featuresInQuestion.Count() > 1 + // ? string.Join("", + // featuresInQuestion.Select( + // (fn, i) => + // T(i == featuresInQuestion.Count() - 1 + // ? "{0}" + // : (i == featuresInQuestion.Count() - 2 + // ? "{0} and " + // : "{0}, "), fn).ToString()).ToArray()) + // : featuresInQuestion.First())); + // } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Features/Startup.cs index e5304842a40..13a04229a77 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/Startup.cs @@ -8,21 +8,20 @@ using OrchardCore.Recipes; using OrchardCore.Security.Permissions; -namespace OrchardCore.Features +namespace OrchardCore.Features; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddRecipeExecutionStep(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddRecipeExecutionStep(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/AllFeaturesDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/AllFeaturesDeploymentStepViewModel.cs index f29f2a07a82..aeb2a6f480c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/AllFeaturesDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/AllFeaturesDeploymentStepViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Features.ViewModels +namespace OrchardCore.Features.ViewModels; + +public class AllFeaturesDeploymentStepViewModel { - public class AllFeaturesDeploymentStepViewModel - { - public bool IgnoreDisabledFeatures { get; set; } - } + public bool IgnoreDisabledFeatures { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/FeaturesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/FeaturesViewModel.cs index 28bc19041a1..38fe07a202c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/FeaturesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/FeaturesViewModel.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; using OrchardCore.Features.Models; -namespace OrchardCore.Features.ViewModels +namespace OrchardCore.Features.ViewModels; + +public class FeaturesViewModel { - public class FeaturesViewModel - { - public string Name { get; set; } + public string Name { get; set; } - /// - /// True when the current tenant is the Default one, and is executing on behalf of other tenant. Otherwise false. - /// - public bool IsProxy { get; set; } + /// + /// True when the current tenant is the Default one, and is executing on behalf of other tenant. Otherwise false. + /// + public bool IsProxy { get; set; } - public IEnumerable Features { get; set; } - } + public IEnumerable Features { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/ModulesIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/ModulesIndexViewModel.cs index 7e85dfe8672..b23cc300fdc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/ModulesIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Features/ViewModels/ModulesIndexViewModel.cs @@ -1,19 +1,18 @@ using System.Collections.Generic; using OrchardCore.Features.Models; -namespace OrchardCore.Features.ViewModels +namespace OrchardCore.Features.ViewModels; + +public class ModulesIndexViewModel { - public class ModulesIndexViewModel - { - public bool InstallModules { get; set; } - public IEnumerable Modules { get; set; } + public bool InstallModules { get; set; } + public IEnumerable Modules { get; set; } - public ModulesIndexOptions Options { get; set; } - public dynamic Pager { get; set; } - } + public ModulesIndexOptions Options { get; set; } + public dynamic Pager { get; set; } +} - public class ModulesIndexOptions - { - public string SearchText { get; set; } - } +public class ModulesIndexOptions +{ + public string SearchText { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Feeds/Controllers/FeedController.cs b/src/OrchardCore.Modules/OrchardCore.Feeds/Controllers/FeedController.cs index 8607f8d1103..324b0f6719b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Feeds/Controllers/FeedController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Feeds/Controllers/FeedController.cs @@ -6,82 +6,81 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Feeds.Models; -namespace OrchardCore.Feeds.Controllers +namespace OrchardCore.Feeds.Controllers; + +public class FeedController : Controller { - public class FeedController : Controller + private readonly IEnumerable _feedFormatProviders; + private readonly IEnumerable _feedQueryProviders; + private readonly IFeedItemBuilder _feedItemBuilder; + private readonly IServiceProvider _serviceProvider; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public FeedController( + IEnumerable feedQueryProviders, + IEnumerable feedFormatProviders, + IFeedItemBuilder feedItemBuilder, + IServiceProvider serviceProvider, + IUpdateModelAccessor updateModelAccessor) { - private readonly IEnumerable _feedFormatProviders; - private readonly IEnumerable _feedQueryProviders; - private readonly IFeedItemBuilder _feedItemBuilder; - private readonly IServiceProvider _serviceProvider; - private readonly IUpdateModelAccessor _updateModelAccessor; + _feedQueryProviders = feedQueryProviders; + _feedFormatProviders = feedFormatProviders; + _feedItemBuilder = feedItemBuilder; + _serviceProvider = serviceProvider; + _updateModelAccessor = updateModelAccessor; + } - public FeedController( - IEnumerable feedQueryProviders, - IEnumerable feedFormatProviders, - IFeedItemBuilder feedItemBuilder, - IServiceProvider serviceProvider, - IUpdateModelAccessor updateModelAccessor) - { - _feedQueryProviders = feedQueryProviders; - _feedFormatProviders = feedFormatProviders; - _feedItemBuilder = feedItemBuilder; - _serviceProvider = serviceProvider; - _updateModelAccessor = updateModelAccessor; - } + public async Task Index(string format) + { + var context = new FeedContext(_updateModelAccessor.ModelUpdater, format); - public async Task Index(string format) + var bestFormatterMatch = _feedFormatProviders + .Select(provider => provider.Match(context)) + .Where(match => match != null && match.FeedBuilder != null) + .MaxBy(match => match.Priority); + + if (bestFormatterMatch == null || bestFormatterMatch.FeedBuilder == null) { - var context = new FeedContext(_updateModelAccessor.ModelUpdater, format); + return NotFound(); + } - var bestFormatterMatch = _feedFormatProviders - .Select(provider => provider.Match(context)) - .Where(match => match != null && match.FeedBuilder != null) - .MaxBy(match => match.Priority); + context.Builder = bestFormatterMatch.FeedBuilder; - if (bestFormatterMatch == null || bestFormatterMatch.FeedBuilder == null) - { - return NotFound(); - } + var queryMatches = new List(); - context.Builder = bestFormatterMatch.FeedBuilder; + foreach (var provider in _feedQueryProviders) + { + queryMatches.Add(await provider.MatchAsync(context)); + } - var queryMatches = new List(); + var bestQueryMatch = queryMatches + .Where(match => match != null && match.FeedQuery != null) + .MaxBy(match => match.Priority); - foreach (var provider in _feedQueryProviders) - { - queryMatches.Add(await provider.MatchAsync(context)); - } + if (bestQueryMatch == null || bestQueryMatch.FeedQuery == null) + { + return NotFound(); + } - var bestQueryMatch = queryMatches - .Where(match => match != null && match.FeedQuery != null) - .MaxBy(match => match.Priority); + var document = await context.Builder.ProcessAsync(context, async () => + { + await bestQueryMatch.FeedQuery.ExecuteAsync(context); - if (bestQueryMatch == null || bestQueryMatch.FeedQuery == null) - { - return NotFound(); - } + await _feedItemBuilder.PopulateAsync(context); - var document = await context.Builder.ProcessAsync(context, async () => + foreach (var contextualizer in context.Response.Contextualizers) { - await bestQueryMatch.FeedQuery.ExecuteAsync(context); - - await _feedItemBuilder.PopulateAsync(context); - - foreach (var contextualizer in context.Response.Contextualizers) + if (ControllerContext != null) { - if (ControllerContext != null) + contextualizer(new ContextualizeContext { - contextualizer(new ContextualizeContext - { - ServiceProvider = _serviceProvider, - Url = Url - }); - } + ServiceProvider = _serviceProvider, + Url = Url + }); } - }); + } + }); - return Content(document.ToString(), "text/xml"); - } + return Content(document.ToString(), "text/xml"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Feeds/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Feeds/Startup.cs index 47470d9b709..942efae5362 100644 --- a/src/OrchardCore.Modules/OrchardCore.Feeds/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Feeds/Startup.cs @@ -2,13 +2,12 @@ using OrchardCore.Feeds; using OrchardCore.Modules; -namespace OrchardCore.Scripting +namespace OrchardCore.Scripting; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddFeeds(); - } + services.AddFeeds(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Controllers/AdminController.cs index 0c639eebe1e..22bb7cae39d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Controllers/AdminController.cs @@ -13,124 +13,123 @@ using OrchardCore.Flows.Models; using OrchardCore.Flows.ViewModels; -namespace OrchardCore.Flows.Controllers +namespace OrchardCore.Flows.Controllers; + +[Admin("Flows/{action}", "Flows.{action}")] +public class AdminController : Controller { - [Admin("Flows/{action}", "Flows.{action}")] - public class AdminController : Controller + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IShapeFactory _shapeFactory; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public AdminController( + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + IContentItemDisplayManager contentItemDisplayManager, + IShapeFactory shapeFactory, + IUpdateModelAccessor updateModelAccessor) { - private readonly IContentManager _contentManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IShapeFactory _shapeFactory; - private readonly IUpdateModelAccessor _updateModelAccessor; - - public AdminController( - IContentManager contentManager, - IContentDefinitionManager contentDefinitionManager, - IContentItemDisplayManager contentItemDisplayManager, - IShapeFactory shapeFactory, - IUpdateModelAccessor updateModelAccessor) - { - _contentManager = contentManager; - _contentDefinitionManager = contentDefinitionManager; - _contentItemDisplayManager = contentItemDisplayManager; - _shapeFactory = shapeFactory; - _updateModelAccessor = updateModelAccessor; - } + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _contentItemDisplayManager = contentItemDisplayManager; + _shapeFactory = shapeFactory; + _updateModelAccessor = updateModelAccessor; + } - public async Task BuildEditor(string id, string prefix, string prefixesName, string contentTypesName, string contentItemsName, string targetId, bool flowMetadata, string parentContentType, string partName) + public async Task BuildEditor(string id, string prefix, string prefixesName, string contentTypesName, string contentItemsName, string targetId, bool flowMetadata, string parentContentType, string partName) + { + if (string.IsNullOrWhiteSpace(id)) { - if (string.IsNullOrWhiteSpace(id)) - { - return NotFound(); - } - - var contentItem = await _contentManager.NewAsync(id); + return NotFound(); + } - // Does this editor need the flow metadata editor? - string cardCollectionType = null; - var colSize = 12; - IEnumerable containedContentTypes = null; + var contentItem = await _contentManager.NewAsync(id); - if (flowMetadata) - { - var metadata = new FlowMetadata(); - contentItem.Weld(metadata); - colSize = (int)Math.Round(metadata.Size / 100.0 * 12); - containedContentTypes = await GetContainedContentTypesAsync(parentContentType, partName); + // Does this editor need the flow metadata editor? + string cardCollectionType = null; + var colSize = 12; + IEnumerable containedContentTypes = null; - cardCollectionType = nameof(FlowPart); - } - else - { - cardCollectionType = nameof(BagPart); - } + if (flowMetadata) + { + var metadata = new FlowMetadata(); + contentItem.Weld(metadata); + colSize = (int)Math.Round(metadata.Size / 100.0 * 12); + containedContentTypes = await GetContainedContentTypesAsync(parentContentType, partName); - // Create a Card Shape - var contentCard = await _shapeFactory.New.ContentCard( - // Updater is the controller for AJAX Requests - Updater: _updateModelAccessor.ModelUpdater, - // Shape Specific - CollectionShapeType: cardCollectionType, - ContentItem: contentItem, - BuildEditor: true, - ParentContentType: parentContentType, - CollectionPartName: partName, - ContainedContentTypes: containedContentTypes, - // Card Specific Properties - TargetId: targetId, - Inline: true, - CanMove: true, - CanDelete: true, - // Input hidden - // Prefixes - PrefixValue: prefix, - PrefixesId: prefixesName.Replace('.', '_'), - PrefixesName: prefixesName, - // ContentTypes - ContentTypesId: contentTypesName.Replace('.', '_'), - ContentTypesName: contentTypesName, - // ContentItems - ContentItemsId: contentItemsName.Replace('.', '_'), - ContentItemsName: contentItemsName - ); - // Only Add ColumnSize Property if Part has FlowMetadata - if (flowMetadata) - { - contentCard.ColumnSize = colSize; - } + cardCollectionType = nameof(FlowPart); + } + else + { + cardCollectionType = nameof(BagPart); + } - var model = new BuildEditorViewModel - { - EditorShape = contentCard - }; - return View("Display", model); + // Create a Card Shape + var contentCard = await _shapeFactory.New.ContentCard( + // Updater is the controller for AJAX Requests + Updater: _updateModelAccessor.ModelUpdater, + // Shape Specific + CollectionShapeType: cardCollectionType, + ContentItem: contentItem, + BuildEditor: true, + ParentContentType: parentContentType, + CollectionPartName: partName, + ContainedContentTypes: containedContentTypes, + // Card Specific Properties + TargetId: targetId, + Inline: true, + CanMove: true, + CanDelete: true, + // Input hidden + // Prefixes + PrefixValue: prefix, + PrefixesId: prefixesName.Replace('.', '_'), + PrefixesName: prefixesName, + // ContentTypes + ContentTypesId: contentTypesName.Replace('.', '_'), + ContentTypesName: contentTypesName, + // ContentItems + ContentItemsId: contentItemsName.Replace('.', '_'), + ContentItemsName: contentItemsName + ); + // Only Add ColumnSize Property if Part has FlowMetadata + if (flowMetadata) + { + contentCard.ColumnSize = colSize; } - private async Task> GetContainedContentTypesAsync(string contentType, string partName) + var model = new BuildEditorViewModel { - var settings = (await _contentDefinitionManager.GetTypeDefinitionAsync(contentType))?.Parts.SingleOrDefault(x => x.Name == partName)?.GetSettings(); + EditorShape = contentCard + }; + return View("Display", model); + } - if (settings?.ContainedContentTypes == null || settings.ContainedContentTypes.Length == 0) - { - return (await _contentDefinitionManager.ListTypeDefinitionsAsync()).Where(t => t.StereotypeEquals("Widget")); - } + private async Task> GetContainedContentTypesAsync(string contentType, string partName) + { + var settings = (await _contentDefinitionManager.GetTypeDefinitionAsync(contentType))?.Parts.SingleOrDefault(x => x.Name == partName)?.GetSettings(); - var definitions = new List(); + if (settings?.ContainedContentTypes == null || settings.ContainedContentTypes.Length == 0) + { + return (await _contentDefinitionManager.ListTypeDefinitionsAsync()).Where(t => t.StereotypeEquals("Widget")); + } - foreach (var ct in settings.ContainedContentTypes) - { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(ct); + var definitions = new List(); - if (definition == null || !definition.StereotypeEquals("Widget")) - { - continue; - } + foreach (var ct in settings.ContainedContentTypes) + { + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(ct); - definitions.Add(definition); + if (definition == null || !definition.StereotypeEquals("Widget")) + { + continue; } - return definitions; + definitions.Add(definition); } + + return definitions; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/BagPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/BagPartDisplayDriver.cs index a87c618601e..4e5ebf4a0ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/BagPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/BagPartDisplayDriver.cs @@ -21,258 +21,257 @@ using OrchardCore.Flows.ViewModels; using OrchardCore.Security.Permissions; -namespace OrchardCore.Flows.Drivers +namespace OrchardCore.Flows.Drivers; + +public sealed class BagPartDisplayDriver : ContentPartDisplayDriver { - public sealed class BagPartDisplayDriver : ContentPartDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IServiceProvider _serviceProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + private readonly INotifier _notifier; + private readonly IAuthorizationService _authorizationService; + + internal readonly IHtmlLocalizer H; + + public BagPartDisplayDriver( + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + IServiceProvider serviceProvider, + IHttpContextAccessor httpContextAccessor, + ILogger logger, + INotifier notifier, + IHtmlLocalizer htmlLocalizer, + IAuthorizationService authorizationService + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IServiceProvider _serviceProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; - private readonly INotifier _notifier; - private readonly IAuthorizationService _authorizationService; - - internal readonly IHtmlLocalizer H; - - public BagPartDisplayDriver( - IContentManager contentManager, - IContentDefinitionManager contentDefinitionManager, - IServiceProvider serviceProvider, - IHttpContextAccessor httpContextAccessor, - ILogger logger, - INotifier notifier, - IHtmlLocalizer htmlLocalizer, - IAuthorizationService authorizationService - ) - { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _serviceProvider = serviceProvider; - _httpContextAccessor = httpContextAccessor; - _logger = logger; - _notifier = notifier; - H = htmlLocalizer; - _authorizationService = authorizationService; - } - - public override IDisplayResult Display(BagPart bagPart, BuildPartDisplayContext context) - { - var hasItems = bagPart.ContentItems.Count > 0; + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _serviceProvider = serviceProvider; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + _notifier = notifier; + H = htmlLocalizer; + _authorizationService = authorizationService; + } - return Initialize(hasItems ? "BagPart" : "BagPart_Empty", m => - { - m.BagPart = bagPart; - m.BuildPartDisplayContext = context; - m.Settings = context.TypePartDefinition.GetSettings(); - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + public override IDisplayResult Display(BagPart bagPart, BuildPartDisplayContext context) + { + var hasItems = bagPart.ContentItems.Count > 0; - public override IDisplayResult Edit(BagPart bagPart, BuildPartEditorContext context) + return Initialize(hasItems ? "BagPart" : "BagPart_Empty", m => { - return Initialize(GetEditorShapeType(context), async m => - { - var contentDefinitionManager = _serviceProvider.GetRequiredService(); - - m.BagPart = bagPart; - m.Updater = context.Updater; - m.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(context.TypePartDefinition); - m.AccessibleWidgets = await GetAccessibleWidgetsAsync(bagPart.ContentItems, contentDefinitionManager); - m.TypePartDefinition = context.TypePartDefinition; - }); - } + m.BagPart = bagPart; + m.BuildPartDisplayContext = context; + m.Settings = context.TypePartDefinition.GetSettings(); + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override async Task UpdateAsync(BagPart part, UpdatePartEditorContext context) + public override IDisplayResult Edit(BagPart bagPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), async m => { - var contentItemDisplayManager = _serviceProvider.GetRequiredService(); var contentDefinitionManager = _serviceProvider.GetRequiredService(); - var model = new BagPartEditViewModel { BagPart = part }; - - await context.Updater.TryUpdateModelAsync(model, Prefix); + m.BagPart = bagPart; + m.Updater = context.Updater; + m.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(context.TypePartDefinition); + m.AccessibleWidgets = await GetAccessibleWidgetsAsync(bagPart.ContentItems, contentDefinitionManager); + m.TypePartDefinition = context.TypePartDefinition; + }); + } - var contentItems = new List(); + public override async Task UpdateAsync(BagPart part, UpdatePartEditorContext context) + { + var contentItemDisplayManager = _serviceProvider.GetRequiredService(); + var contentDefinitionManager = _serviceProvider.GetRequiredService(); - // Handle the content found in the request - for (var i = 0; i < model.Prefixes.Length; i++) - { - var contentItem = await _contentManager.NewAsync(model.ContentTypes[i]); + var model = new BagPartEditViewModel { BagPart = part }; - // assign the owner of the item to ensure we can validate access to it later. - contentItem.Owner = GetCurrentOwner(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - // Try to match the requested id with an existing id - var existingContentItem = part.ContentItems.FirstOrDefault(x => string.Equals(x.ContentItemId, model.ContentItems[i], StringComparison.OrdinalIgnoreCase)); + var contentItems = new List(); - if (existingContentItem == null && !await AuthorizeAsync(contentDefinitionManager, CommonPermissions.EditContent, contentItem)) - { - // at this point the user is somehow trying to add content with no privileges. ignore the request - continue; - } + // Handle the content found in the request + for (var i = 0; i < model.Prefixes.Length; i++) + { + var contentItem = await _contentManager.NewAsync(model.ContentTypes[i]); - // When the content item already exists merge its elements to preserve nested content item ids. - // All of the data for these merged items is then replaced by the model values on update, while a nested content item id is maintained. - // This prevents nested items which rely on the content item id, i.e. the media attached field, losing their reference point. - if (existingContentItem != null) - { - if (!await AuthorizeAsync(contentDefinitionManager, CommonPermissions.EditContent, existingContentItem)) - { - // at this point the user is somehow modifying existing content with no privileges. - // honor the existing data and ignore the data in the request - contentItems.Add(existingContentItem); - - continue; - } - - // at this point the user have privileges to edit, merge the data from the request - contentItem.ContentItemId = model.ContentItems[i]; - contentItem.Merge(existingContentItem); - } + // assign the owner of the item to ensure we can validate access to it later. + contentItem.Owner = GetCurrentOwner(); - var widgetModel = await contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, context.IsNew, htmlFieldPrefix: model.Prefixes[i]); + // Try to match the requested id with an existing id + var existingContentItem = part.ContentItems.FirstOrDefault(x => string.Equals(x.ContentItemId, model.ContentItems[i], StringComparison.OrdinalIgnoreCase)); - contentItems.Add(contentItem); + if (existingContentItem == null && !await AuthorizeAsync(contentDefinitionManager, CommonPermissions.EditContent, contentItem)) + { + // at this point the user is somehow trying to add content with no privileges. ignore the request + continue; } - // at the end, lets add existing readonly contents. - foreach (var existingContentItem in part.ContentItems) + // When the content item already exists merge its elements to preserve nested content item ids. + // All of the data for these merged items is then replaced by the model values on update, while a nested content item id is maintained. + // This prevents nested items which rely on the content item id, i.e. the media attached field, losing their reference point. + if (existingContentItem != null) { - if (contentItems.Any(x => x.ContentItemId == existingContentItem.ContentItemId)) + if (!await AuthorizeAsync(contentDefinitionManager, CommonPermissions.EditContent, existingContentItem)) { - // item was already added using the edit + // at this point the user is somehow modifying existing content with no privileges. + // honor the existing data and ignore the data in the request + contentItems.Add(existingContentItem); continue; } - if (await AuthorizeAsync(contentDefinitionManager, CommonPermissions.DeleteContent, existingContentItem)) - { - // at this point the user has permission to delete a securable item or the type isn't securable - // if the existing content id isn't in the requested ids, don't add the content item... meaning the user deleted it - if (!model.ContentItems.Contains(existingContentItem.ContentItemId)) - { - continue; - } - } - - // since the content item isn't editable, lets add it so it's not removed from the collection - contentItems.Add(existingContentItem); + // at this point the user have privileges to edit, merge the data from the request + contentItem.ContentItemId = model.ContentItems[i]; + contentItem.Merge(existingContentItem); } - // TODO, some how here contentItems should be sorted by a defined order - part.ContentItems = contentItems; + var widgetModel = await contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, context.IsNew, htmlFieldPrefix: model.Prefixes[i]); - return Edit(part, context); + contentItems.Add(contentItem); } - private async Task> GetAccessibleWidgetsAsync(IEnumerable contentItems, IContentDefinitionManager contentDefinitionManager) + // at the end, lets add existing readonly contents. + foreach (var existingContentItem in part.ContentItems) { - var widgets = new List(); - - foreach (var contentItem in contentItems) + if (contentItems.Any(x => x.ContentItemId == existingContentItem.ContentItemId)) { - var widget = new BagPartWidgetViewModel - { - ContentItem = contentItem, - Viewable = true, - Editable = true, - Deletable = true, - }; + // item was already added using the edit - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + continue; + } - if (contentTypeDefinition == null) + if (await AuthorizeAsync(contentDefinitionManager, CommonPermissions.DeleteContent, existingContentItem)) + { + // at this point the user has permission to delete a securable item or the type isn't securable + // if the existing content id isn't in the requested ids, don't add the content item... meaning the user deleted it + if (!model.ContentItems.Contains(existingContentItem.ContentItemId)) { - _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", contentItem.ContentItemId, contentItem.ContentType); - - await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", contentItem.ContentItemId, contentItem.ContentType]); - continue; } + } - if (contentTypeDefinition.IsSecurable()) - { - widget.Viewable = await AuthorizeAsync(CommonPermissions.ViewContent, contentItem); - widget.Editable = await AuthorizeAsync(CommonPermissions.EditContent, contentItem); - widget.Deletable = await AuthorizeAsync(CommonPermissions.DeleteContent, contentItem); - } + // since the content item isn't editable, lets add it so it's not removed from the collection + contentItems.Add(existingContentItem); + } - widget.ContentTypeDefinition = contentTypeDefinition; + // TODO, some how here contentItems should be sorted by a defined order + part.ContentItems = contentItems; - if (widget.Editable || widget.Viewable) - { - widgets.Add(widget); - } - } + return Edit(part, context); + } - return widgets; - } + private async Task> GetAccessibleWidgetsAsync(IEnumerable contentItems, IContentDefinitionManager contentDefinitionManager) + { + var widgets = new List(); - private async Task AuthorizeAsync(IContentDefinitionManager contentDefinitionManager, Permission permission, ContentItem contentItem) + foreach (var contentItem in contentItems) { - var contentType = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - - if (contentType?.IsSecurable() ?? false) + var widget = new BagPartWidgetViewModel { - return true; - } + ContentItem = contentItem, + Viewable = true, + Editable = true, + Deletable = true, + }; - return await AuthorizeAsync(permission, contentItem); - } + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - private Task AuthorizeAsync(Permission permission, ContentItem contentItem) - => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, permission, contentItem); + if (contentTypeDefinition == null) + { + _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", contentItem.ContentItemId, contentItem.ContentType); - private async Task> GetContainedContentTypesAsync(ContentTypePartDefinition typePartDefinition) - { - var settings = typePartDefinition.GetSettings(); - var contentTypes = Enumerable.Empty(); + await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", contentItem.ContentItemId, contentItem.ContentType]); - if (settings.ContainedStereotypes != null && settings.ContainedStereotypes.Length > 0) + continue; + } + + if (contentTypeDefinition.IsSecurable()) { - contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(contentType => contentType.HasStereotype() && settings.ContainedStereotypes.Contains(contentType.GetStereotype(), StringComparer.OrdinalIgnoreCase)); + widget.Viewable = await AuthorizeAsync(CommonPermissions.ViewContent, contentItem); + widget.Editable = await AuthorizeAsync(CommonPermissions.EditContent, contentItem); + widget.Deletable = await AuthorizeAsync(CommonPermissions.DeleteContent, contentItem); } - else if (settings.ContainedContentTypes != null && settings.ContainedContentTypes.Length > 0) + + widget.ContentTypeDefinition = contentTypeDefinition; + + if (widget.Editable || widget.Viewable) { - var definitions = new List(); + widgets.Add(widget); + } + } - foreach (var contentType in settings.ContainedContentTypes) - { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); + return widgets; + } - if (definition == null) - { - continue; - } + private async Task AuthorizeAsync(IContentDefinitionManager contentDefinitionManager, Permission permission, ContentItem contentItem) + { + var contentType = await contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - definitions.Add(definition); - } + if (contentType?.IsSecurable() ?? false) + { + return true; + } - contentTypes = definitions; - } + return await AuthorizeAsync(permission, contentItem); + } + + private Task AuthorizeAsync(Permission permission, ContentItem contentItem) + => _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, permission, contentItem); - var user = _httpContextAccessor.HttpContext.User; + private async Task> GetContainedContentTypesAsync(ContentTypePartDefinition typePartDefinition) + { + var settings = typePartDefinition.GetSettings(); + var contentTypes = Enumerable.Empty(); - var accessibleContentTypes = new List(); + if (settings.ContainedStereotypes != null && settings.ContainedStereotypes.Length > 0) + { + contentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(contentType => contentType.HasStereotype() && settings.ContainedStereotypes.Contains(contentType.GetStereotype(), StringComparer.OrdinalIgnoreCase)); + } + else if (settings.ContainedContentTypes != null && settings.ContainedContentTypes.Length > 0) + { + var definitions = new List(); - foreach (var contentType in contentTypes) + foreach (var contentType in settings.ContainedContentTypes) { - if (contentType.IsSecurable() && !await _authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.EditContent, contentType, GetCurrentOwner())) + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); + + if (definition == null) { continue; } - accessibleContentTypes.Add(contentType); + definitions.Add(definition); } - return accessibleContentTypes; + contentTypes = definitions; } - private string GetCurrentOwner() + var user = _httpContextAccessor.HttpContext.User; + + var accessibleContentTypes = new List(); + + foreach (var contentType in contentTypes) { - return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (contentType.IsSecurable() && !await _authorizationService.AuthorizeContentTypeAsync(user, CommonPermissions.EditContent, contentType, GetCurrentOwner())) + { + continue; + } + + accessibleContentTypes.Add(contentType); } + + return accessibleContentTypes; + } + + private string GetCurrentOwner() + { + return _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowMetadataDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowMetadataDisplayDriver.cs index 5aa9fcda764..a4b763ad8ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowMetadataDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowMetadataDisplayDriver.cs @@ -5,38 +5,37 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.Drivers +namespace OrchardCore.Flows.Drivers; + +public sealed class FlowMetadataDisplayDriver : ContentDisplayDriver { - public sealed class FlowMetadataDisplayDriver : ContentDisplayDriver + public override IDisplayResult Edit(ContentItem model, BuildEditorContext context) { - public override IDisplayResult Edit(ContentItem model, BuildEditorContext context) + var flowMetadata = model.As(); + + if (flowMetadata == null) { - var flowMetadata = model.As(); - - if (flowMetadata == null) - { - return null; - } - - return Initialize("FlowMetadata_Edit", m => - { - m.Alignment = flowMetadata.Alignment; - m.Size = flowMetadata.Size; - }).Location("Footer"); + return null; } - public override async Task UpdateAsync(ContentItem contentItem, UpdateEditorContext context) + return Initialize("FlowMetadata_Edit", m => { - var flowMetadata = contentItem.As(); - - if (flowMetadata == null) - { - return null; - } + m.Alignment = flowMetadata.Alignment; + m.Size = flowMetadata.Size; + }).Location("Footer"); + } - await contentItem.AlterAsync(model => context.Updater.TryUpdateModelAsync(model, Prefix)); + public override async Task UpdateAsync(ContentItem contentItem, UpdateEditorContext context) + { + var flowMetadata = contentItem.As(); - return Edit(contentItem, context); + if (flowMetadata == null) + { + return null; } + + await contentItem.AlterAsync(model => context.Updater.TryUpdateModelAsync(model, Prefix)); + + return Edit(contentItem, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowPartDisplayDriver.cs index 86c80194e65..145cc131002 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Drivers/FlowPartDisplayDriver.cs @@ -16,131 +16,130 @@ using OrchardCore.Flows.Models; using OrchardCore.Flows.ViewModels; -namespace OrchardCore.Flows.Drivers +namespace OrchardCore.Flows.Drivers; + +public sealed class FlowPartDisplayDriver : ContentPartDisplayDriver { - public sealed class FlowPartDisplayDriver : ContentPartDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IServiceProvider _serviceProvider; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + internal readonly IHtmlLocalizer H; + + public FlowPartDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IServiceProvider serviceProvider, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + ILogger logger + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IServiceProvider _serviceProvider; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - internal readonly IHtmlLocalizer H; - - public FlowPartDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - IServiceProvider serviceProvider, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - ILogger logger - ) - { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _serviceProvider = serviceProvider; - H = htmlLocalizer; - _notifier = notifier; - _logger = logger; - } + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _serviceProvider = serviceProvider; + H = htmlLocalizer; + _notifier = notifier; + _logger = logger; + } - public override IDisplayResult Display(FlowPart flowPart, BuildPartDisplayContext context) - { - var hasItems = flowPart.Widgets.Count > 0; + public override IDisplayResult Display(FlowPart flowPart, BuildPartDisplayContext context) + { + var hasItems = flowPart.Widgets.Count > 0; - return Initialize(hasItems ? "FlowPart" : "FlowPart_Empty", m => - { - m.FlowPart = flowPart; - m.BuildPartDisplayContext = context; - }) - .Location("Detail", "Content"); - } + return Initialize(hasItems ? "FlowPart" : "FlowPart_Empty", m => + { + m.FlowPart = flowPart; + m.BuildPartDisplayContext = context; + }) + .Location("Detail", "Content"); + } - public override IDisplayResult Edit(FlowPart flowPart, BuildPartEditorContext context) + public override IDisplayResult Edit(FlowPart flowPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - var containedContentTypes = await GetContainedContentTypesAsync(context.TypePartDefinition); - var notify = false; + var containedContentTypes = await GetContainedContentTypesAsync(context.TypePartDefinition); + var notify = false; - var existingWidgets = new List(); + var existingWidgets = new List(); - foreach (var widget in flowPart.Widgets) + foreach (var widget in flowPart.Widgets) + { + if (!containedContentTypes.Any(c => c.Name == widget.ContentType)) { - if (!containedContentTypes.Any(c => c.Name == widget.ContentType)) - { - _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType); - await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType]); - notify = true; - } - else - { - existingWidgets.Add(widget); - } + _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType); + await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType]); + notify = true; } - - flowPart.Widgets = existingWidgets; - - if (notify) + else { - await _notifier.WarningAsync(H["Publishing this content item may erase created content. Fix any content type issues beforehand."]); + existingWidgets.Add(widget); } + } - model.FlowPart = flowPart; - model.Updater = context.Updater; - model.ContainedContentTypeDefinitions = containedContentTypes; - }); - } - - public override async Task UpdateAsync(FlowPart part, UpdatePartEditorContext context) - { - var contentItemDisplayManager = _serviceProvider.GetRequiredService(); + flowPart.Widgets = existingWidgets; - var model = new FlowPartEditViewModel { FlowPart = part }; + if (notify) + { + await _notifier.WarningAsync(H["Publishing this content item may erase created content. Fix any content type issues beforehand."]); + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + model.FlowPart = flowPart; + model.Updater = context.Updater; + model.ContainedContentTypeDefinitions = containedContentTypes; + }); + } - var contentItems = new List(); + public override async Task UpdateAsync(FlowPart part, UpdatePartEditorContext context) + { + var contentItemDisplayManager = _serviceProvider.GetRequiredService(); - for (var i = 0; i < model.Prefixes.Length; i++) - { - var contentItem = await _contentManager.NewAsync(model.ContentTypes[i]); - var existingContentItem = part.Widgets.FirstOrDefault(x => string.Equals(x.ContentItemId, model.ContentItems[i], StringComparison.OrdinalIgnoreCase)); + var model = new FlowPartEditViewModel { FlowPart = part }; - // When the content item already exists merge its elements to reverse nested content item ids. - // All of the data for these merged items is then replaced by the model values on update, while a nested content item id is maintained. - // This prevents nested items which rely on the content item id, i.e. the media attached field, losing their reference point. - if (existingContentItem != null) - { - contentItem.ContentItemId = model.ContentItems[i]; - contentItem.Merge(existingContentItem); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - contentItem.Weld(new FlowMetadata()); + var contentItems = new List(); - var widgetModel = await contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, context.IsNew, htmlFieldPrefix: model.Prefixes[i]); + for (var i = 0; i < model.Prefixes.Length; i++) + { + var contentItem = await _contentManager.NewAsync(model.ContentTypes[i]); + var existingContentItem = part.Widgets.FirstOrDefault(x => string.Equals(x.ContentItemId, model.ContentItems[i], StringComparison.OrdinalIgnoreCase)); - contentItems.Add(contentItem); + // When the content item already exists merge its elements to reverse nested content item ids. + // All of the data for these merged items is then replaced by the model values on update, while a nested content item id is maintained. + // This prevents nested items which rely on the content item id, i.e. the media attached field, losing their reference point. + if (existingContentItem != null) + { + contentItem.ContentItemId = model.ContentItems[i]; + contentItem.Merge(existingContentItem); } - part.Widgets = contentItems; + contentItem.Weld(new FlowMetadata()); - return Edit(part, context); + var widgetModel = await contentItemDisplayManager.UpdateEditorAsync(contentItem, context.Updater, context.IsNew, htmlFieldPrefix: model.Prefixes[i]); + + contentItems.Add(contentItem); } - private async Task> GetContainedContentTypesAsync(ContentTypePartDefinition typePartDefinition) - { - var settings = typePartDefinition.GetSettings(); + part.Widgets = contentItems; - if (settings?.ContainedContentTypes == null || settings.ContainedContentTypes.Length == 0) - { - return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(t => t.StereotypeEquals("Widget")); - } + return Edit(part, context); + } + private async Task> GetContainedContentTypesAsync(ContentTypePartDefinition typePartDefinition) + { + var settings = typePartDefinition.GetSettings(); + + if (settings?.ContainedContentTypes == null || settings.ContainedContentTypes.Length == 0) + { return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(t => settings.ContainedContentTypes.Contains(t.Name) && t.StereotypeEquals("Widget")); + .Where(t => t.StereotypeEquals("Widget")); } + + return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(t => settings.ContainedContentTypes.Contains(t.Name) && t.StereotypeEquals("Widget")); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs index bd7d7bd17af..c559c1f6596 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/BagPartQueryObjectType.cs @@ -6,19 +6,18 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +public class BagPartQueryObjectType : ObjectGraphType { - public class BagPartQueryObjectType : ObjectGraphType + public BagPartQueryObjectType(IStringLocalizer S) { - public BagPartQueryObjectType(IStringLocalizer S) - { - Name = "BagPart"; - Description = S["A BagPart allows to add content items directly within another content item"]; + Name = "BagPart"; + Description = S["A BagPart allows to add content items directly within another content item"]; - Field, IEnumerable>("contentItems") - .Description("the content items") - .PagingArguments() - .Resolve(x => x.Page(x.Source.ContentItems)); - } + Field, IEnumerable>("contentItems") + .Description("the content items") + .PagingArguments() + .Resolve(x => x.Page(x.Source.ContentItems)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs index e0b3397bbd2..52b78698012 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowAlignmentEnum.cs @@ -1,18 +1,17 @@ using GraphQL.Types; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +public class FlowAlignmentEnum : EnumerationGraphType { - public class FlowAlignmentEnum : EnumerationGraphType + public FlowAlignmentEnum() { - public FlowAlignmentEnum() - { - Name = "FlowAlignment"; + Name = "FlowAlignment"; - Description = "The widget alignment."; - Add("Left", 0, "Left alignment."); - Add("Center", 1, "Center alignment."); - Add("Right", 2, "Right alignment."); - Add("Justify", 3, "Justify alignment."); - } + Description = "The widget alignment."; + Add("Left", 0, "Left alignment."); + Add("Center", 1, "Center alignment."); + Add("Right", 2, "Right alignment."); + Add("Justify", 3, "Justify alignment."); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataContentTypeBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataContentTypeBuilder.cs index 697104e9050..25119a26ccc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataContentTypeBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataContentTypeBuilder.cs @@ -4,19 +4,18 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +public class FlowMetadataContentTypeBuilder : IContentTypeBuilder { - public class FlowMetadataContentTypeBuilder : IContentTypeBuilder + public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType) { - public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType) + if (contentTypeDefinition.GetStereotype() != "Widget") { - if (contentTypeDefinition.GetStereotype() != "Widget") - { - return; - } - - contentItemType.Field("metadata") - .Resolve(context => context.Source.As()); + return; } + + contentItemType.Field("metadata") + .Resolve(context => context.Source.As()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataQueryObjectType.cs index 91db64109ec..6f7a422621e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowMetadataQueryObjectType.cs @@ -1,16 +1,15 @@ using GraphQL.Types; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +public class FlowMetadataQueryObjectType : ObjectGraphType { - public class FlowMetadataQueryObjectType : ObjectGraphType + public FlowMetadataQueryObjectType() { - public FlowMetadataQueryObjectType() - { - Name = "FlowMetadata"; + Name = "FlowMetadata"; - Field(x => x.Size, nullable: true); - Field("alignment"); - } + Field(x => x.Size, nullable: true); + Field("alignment"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs index 7f3019906be..04ef713cd5d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/FlowPartQueryObjectType.cs @@ -3,18 +3,17 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +public class FlowPartQueryObjectType : ObjectGraphType { - public class FlowPartQueryObjectType : ObjectGraphType + public FlowPartQueryObjectType(IStringLocalizer S) { - public FlowPartQueryObjectType(IStringLocalizer S) - { - Name = "FlowPart"; - Description = S["A FlowPart allows to add content items directly within another content item"]; + Name = "FlowPart"; + Description = S["A FlowPart allows to add content items directly within another content item"]; - Field>("widgets") - .Description("The widgets.") - .Resolve(context => context.Source.Widgets); - } + Field>("widgets") + .Description("The widgets.") + .Resolve(context => context.Source.Widgets); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/Startup.cs index 80d88ed5e6c..2a206710b73 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/GraphQL/Startup.cs @@ -4,18 +4,17 @@ using OrchardCore.Flows.Models; using OrchardCore.Modules; -namespace OrchardCore.Flows.GraphQL +namespace OrchardCore.Flows.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Handlers/BagPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Handlers/BagPartHandler.cs index f26e5f44f01..525580dfbc0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Handlers/BagPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Handlers/BagPartHandler.cs @@ -4,23 +4,22 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.Handlers +namespace OrchardCore.Flows.Handlers; + +public class BagPartHandler : ContentPartHandler { - public class BagPartHandler : ContentPartHandler + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, BagPart part) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, BagPart part) + return context.ForAsync(aspect => { - return context.ForAsync(aspect => + aspect.Accessors.Add((jsonObject) => { - aspect.Accessors.Add((jsonObject) => - { - // Content.Path contains the accessor for named bag parts and typed bag parts. - var jContent = (JsonObject)part.Content; - return jsonObject[jContent.GetNormalizedPath()]["ContentItems"] as JsonArray; - }); - - return Task.CompletedTask; + // Content.Path contains the accessor for named bag parts and typed bag parts. + var jContent = (JsonObject)part.Content; + return jsonObject[jContent.GetNormalizedPath()]["ContentItems"] as JsonArray; }); - } + + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/BagPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/BagPartIndexHandler.cs index 928643d7994..1f7fa898cdb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/BagPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/BagPartIndexHandler.cs @@ -5,48 +5,47 @@ using OrchardCore.Flows.Models; using OrchardCore.Indexing; -namespace OrchardCore.Flows.Indexing +namespace OrchardCore.Flows.Indexing; + +public class BagPartIndexHandler : ContentPartIndexHandler { - public class BagPartIndexHandler : ContentPartIndexHandler + private readonly IServiceProvider _serviceProvider; + + public BagPartIndexHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public BagPartIndexHandler(IServiceProvider serviceProvider) + public override async Task BuildIndexAsync(BagPart bagPart, BuildPartIndexContext context) + { + var options = context.Settings.ToOptions(); + if (options == DocumentIndexOptions.None) { - _serviceProvider = serviceProvider; + return; } - public override async Task BuildIndexAsync(BagPart bagPart, BuildPartIndexContext context) + if (bagPart.ContentItems.Count != 0) { - var options = context.Settings.ToOptions(); - if (options == DocumentIndexOptions.None) - { - return; - } + // Lazy resolution to prevent cyclic dependency. + var contentItemIndexHandlers = _serviceProvider.GetServices(); - if (bagPart.ContentItems.Count != 0) + foreach (var contentItemIndexHandler in contentItemIndexHandlers) { - // Lazy resolution to prevent cyclic dependency. - var contentItemIndexHandlers = _serviceProvider.GetServices(); - - foreach (var contentItemIndexHandler in contentItemIndexHandlers) + foreach (var contentItem in bagPart.ContentItems) { - foreach (var contentItem in bagPart.ContentItems) + var keys = new List { - var keys = new List - { - contentItem.ContentType, - }; + contentItem.ContentType, + }; - foreach (var key in context.Keys) - { - keys.Add($"{key}.{contentItem.ContentType}"); - } + foreach (var key in context.Keys) + { + keys.Add($"{key}.{contentItem.ContentType}"); + } - var buildIndexContext = new BuildIndexContext(context.DocumentIndex, contentItem, keys, context.Settings); + var buildIndexContext = new BuildIndexContext(context.DocumentIndex, contentItem, keys, context.Settings); - await contentItemIndexHandler.BuildIndexAsync(buildIndexContext); - } + await contentItemIndexHandler.BuildIndexAsync(buildIndexContext); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/FlowPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/FlowPartIndexHandler.cs index 11597f9d0f3..c11a1ffa859 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/FlowPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Indexing/FlowPartIndexHandler.cs @@ -5,48 +5,47 @@ using OrchardCore.Flows.Models; using OrchardCore.Indexing; -namespace OrchardCore.Flows.Indexing +namespace OrchardCore.Flows.Indexing; + +public class FlowPartIndexHandler : ContentPartIndexHandler { - public class FlowPartIndexHandler : ContentPartIndexHandler + private readonly IServiceProvider _serviceProvider; + + public FlowPartIndexHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public FlowPartIndexHandler(IServiceProvider serviceProvider) + public override async Task BuildIndexAsync(FlowPart FlowPart, BuildPartIndexContext context) + { + var options = context.Settings.ToOptions(); + if (options == DocumentIndexOptions.None) { - _serviceProvider = serviceProvider; + return; } - public override async Task BuildIndexAsync(FlowPart FlowPart, BuildPartIndexContext context) + if (FlowPart.Widgets.Count != 0) { - var options = context.Settings.ToOptions(); - if (options == DocumentIndexOptions.None) - { - return; - } + // Lazy resolution to prevent cyclic dependency. + var contentItemIndexHandlers = _serviceProvider.GetServices(); - if (FlowPart.Widgets.Count != 0) + foreach (var contentItemIndexHandler in contentItemIndexHandlers) { - // Lazy resolution to prevent cyclic dependency. - var contentItemIndexHandlers = _serviceProvider.GetServices(); - - foreach (var contentItemIndexHandler in contentItemIndexHandlers) + foreach (var contentItem in FlowPart.Widgets) { - foreach (var contentItem in FlowPart.Widgets) + var keys = new List { - var keys = new List - { - contentItem.ContentType, - }; + contentItem.ContentType, + }; - foreach (var key in context.Keys) - { - keys.Add($"{key}.{contentItem.ContentType}"); - } + foreach (var key in context.Keys) + { + keys.Add($"{key}.{contentItem.ContentType}"); + } - var buildIndexContext = new BuildIndexContext(context.DocumentIndex, contentItem, keys, context.Settings); + var buildIndexContext = new BuildIndexContext(context.DocumentIndex, contentItem, keys, context.Settings); - await contentItemIndexHandler.BuildIndexAsync(buildIndexContext); - } + await contentItemIndexHandler.BuildIndexAsync(buildIndexContext); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Migrations.cs index 37e6f0250b0..c4a8c830ab6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Migrations.cs @@ -4,50 +4,49 @@ using OrchardCore.Data.Migration; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows +namespace OrchardCore.Flows; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public async Task CreateAsync() { - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } - - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("FlowPart", builder => builder - .Attachable() - .WithDescription("Provides a customizable body for your content item where you can build a content structure with widgets.")); - - await _contentDefinitionManager.AlterPartDefinitionAsync("BagPart", builder => builder - .Attachable() - .Reusable() - .WithDescription("Provides a collection behavior for your content item where you can place other content items.")); - - // Shortcut other migration steps on new content definition schemas. - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("BagPart", builder => builder - .Attachable() - .Reusable() - .WithDescription("Provides a collection behavior for your content item where you can place other content items.")); - - return 2; - } - - // Migrate PartSettings. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await _contentDefinitionManager.MigratePartSettingsAsync(); - - return 3; - } + await _contentDefinitionManager.AlterPartDefinitionAsync("FlowPart", builder => builder + .Attachable() + .WithDescription("Provides a customizable body for your content item where you can build a content structure with widgets.")); + + await _contentDefinitionManager.AlterPartDefinitionAsync("BagPart", builder => builder + .Attachable() + .Reusable() + .WithDescription("Provides a collection behavior for your content item where you can place other content items.")); + + // Shortcut other migration steps on new content definition schemas. + return 3; + } + + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("BagPart", builder => builder + .Attachable() + .Reusable() + .WithDescription("Provides a collection behavior for your content item where you can place other content items.")); + + return 2; + } + + // Migrate PartSettings. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await _contentDefinitionManager.MigratePartSettingsAsync(); + + return 3; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPart.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPart.cs index 7b5ed798795..b1c07518afa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPart.cs @@ -2,11 +2,10 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement; -namespace OrchardCore.Flows.Models +namespace OrchardCore.Flows.Models; + +public class BagPart : ContentPart { - public class BagPart : ContentPart - { - [BindNever] - public List ContentItems { get; set; } = []; - } + [BindNever] + public List ContentItems { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPartSettings.cs index 03944116cb6..9cfa19df96d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Models/BagPartSettings.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Flows.Models +namespace OrchardCore.Flows.Models; + +public class BagPartSettings { - public class BagPartSettings - { - public string[] ContainedContentTypes { get; set; } = []; + public string[] ContainedContentTypes { get; set; } = []; - public string[] ContainedStereotypes { get; set; } = []; + public string[] ContainedStereotypes { get; set; } = []; - public string DisplayType { get; set; } - } + public string DisplayType { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowMetadata.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowMetadata.cs index b3e1c1fbd98..f5408160a3a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowMetadata.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowMetadata.cs @@ -1,18 +1,17 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Flows.Models +namespace OrchardCore.Flows.Models; + +public enum FlowAlignment { - public enum FlowAlignment - { - Left, - Center, - Right, - Justify - } + Left, + Center, + Right, + Justify +} - public class FlowMetadata : ContentPart - { - public FlowAlignment Alignment { get; set; } = FlowAlignment.Justify; - public int Size { get; set; } = 100; - } +public class FlowMetadata : ContentPart +{ + public FlowAlignment Alignment { get; set; } = FlowAlignment.Justify; + public int Size { get; set; } = 100; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPart.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPart.cs index 8a745e952db..6e1c43e6647 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPart.cs @@ -2,11 +2,10 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement; -namespace OrchardCore.Flows.Models +namespace OrchardCore.Flows.Models; + +public class FlowPart : ContentPart { - public class FlowPart : ContentPart - { - [BindNever] - public List Widgets { get; set; } = []; - } + [BindNever] + public List Widgets { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPartSettings.cs index b6170a25dc4..88f38a0b8f5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Models/FlowPartSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Flows.Models +namespace OrchardCore.Flows.Models; + +public class FlowPartSettings { - public class FlowPartSettings - { - public string[] ContainedContentTypes { get; set; } = []; - } + public string[] ContainedContentTypes { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Settings/BagPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Settings/BagPartSettingsDisplayDriver.cs index 8943269207d..52e59a689cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Settings/BagPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Settings/BagPartSettingsDisplayDriver.cs @@ -10,99 +10,98 @@ using OrchardCore.Flows.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Flows.Settings -{ - public sealed class BagPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; +namespace OrchardCore.Flows.Settings; - internal readonly IStringLocalizer S; +public sealed class BagPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver +{ + private readonly IContentDefinitionManager _contentDefinitionManager; - public BagPartSettingsDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - IStringLocalizer localizer) - { - _contentDefinitionManager = contentDefinitionManager; - S = localizer; - } + internal readonly IStringLocalizer S; - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) - { - return Initialize("BagPartSettings_Edit", async model => - { - var settings = contentTypePartDefinition.GetSettings(); - - model.BagPartSettings = settings; - model.ContainedContentTypes = model.BagPartSettings.ContainedContentTypes; - model.DisplayType = model.BagPartSettings.DisplayType; - model.ContentTypes = []; - model.Source = settings.ContainedStereotypes != null && settings.ContainedStereotypes.Length > 0 ? BagPartSettingType.Stereotypes : BagPartSettingType.ContentTypes; - model.Stereotypes = string.Join(',', settings.ContainedStereotypes ?? []); - foreach (var contentTypeDefinition in await _contentDefinitionManager.ListTypeDefinitionsAsync()) - { - model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); - } - }).Location("Content"); - } + public BagPartSettingsDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + IStringLocalizer localizer) + { + _contentDefinitionManager = contentDefinitionManager; + S = localizer; + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("BagPartSettings_Edit", async model => { - var model = new BagPartSettingsViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.ContainedContentTypes, - m => m.DisplayType, - m => m.Source, - m => m.Stereotypes); + var settings = contentTypePartDefinition.GetSettings(); - switch (model.Source) + model.BagPartSettings = settings; + model.ContainedContentTypes = model.BagPartSettings.ContainedContentTypes; + model.DisplayType = model.BagPartSettings.DisplayType; + model.ContentTypes = []; + model.Source = settings.ContainedStereotypes != null && settings.ContainedStereotypes.Length > 0 ? BagPartSettingType.Stereotypes : BagPartSettingType.ContentTypes; + model.Stereotypes = string.Join(',', settings.ContainedStereotypes ?? []); + foreach (var contentTypeDefinition in await _contentDefinitionManager.ListTypeDefinitionsAsync()) { - case BagPartSettingType.ContentTypes: - SetContentTypes(context, model); - break; - case BagPartSettingType.Stereotypes: - SetStereoTypes(context, model); - break; - default: - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Source), S["Content type source must be set with a valid value."]); - break; + model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); } + }).Location("Content"); + } - return Edit(contentTypePartDefinition, context); - } + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new BagPartSettingsViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.ContainedContentTypes, + m => m.DisplayType, + m => m.Source, + m => m.Stereotypes); - private void SetStereoTypes(UpdateTypePartEditorContext context, BagPartSettingsViewModel model) + switch (model.Source) { - if (string.IsNullOrEmpty(model.Stereotypes)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Stereotypes), S["Please provide a Stereotype."]); + case BagPartSettingType.ContentTypes: + SetContentTypes(context, model); + break; + case BagPartSettingType.Stereotypes: + SetStereoTypes(context, model); + break; + default: + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Source), S["Content type source must be set with a valid value."]); + break; + } - return; - } + return Edit(contentTypePartDefinition, context); + } - context.Builder.WithSettings(new BagPartSettings - { - ContainedContentTypes = [], - ContainedStereotypes = model.Stereotypes.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries), - DisplayType = model.DisplayType - }); + private void SetStereoTypes(UpdateTypePartEditorContext context, BagPartSettingsViewModel model) + { + if (string.IsNullOrEmpty(model.Stereotypes)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Stereotypes), S["Please provide a Stereotype."]); + + return; } - private void SetContentTypes(UpdateTypePartEditorContext context, BagPartSettingsViewModel model) + context.Builder.WithSettings(new BagPartSettings { - if (model.ContainedContentTypes == null || model.ContainedContentTypes.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.ContainedContentTypes), S["At least one content type must be selected."]); + ContainedContentTypes = [], + ContainedStereotypes = model.Stereotypes.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries), + DisplayType = model.DisplayType + }); + } - return; - } + private void SetContentTypes(UpdateTypePartEditorContext context, BagPartSettingsViewModel model) + { + if (model.ContainedContentTypes == null || model.ContainedContentTypes.Length == 0) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.ContainedContentTypes), S["At least one content type must be selected."]); - context.Builder.WithSettings(new BagPartSettings - { - ContainedContentTypes = model.ContainedContentTypes, - ContainedStereotypes = [], - DisplayType = model.DisplayType - }); + return; } + + context.Builder.WithSettings(new BagPartSettings + { + ContainedContentTypes = model.ContainedContentTypes, + ContainedStereotypes = [], + DisplayType = model.DisplayType + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Settings/FlowPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Settings/FlowPartSettingsDisplayDriver.cs index 2570e113123..96328023e0f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Settings/FlowPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Settings/FlowPartSettingsDisplayDriver.cs @@ -8,45 +8,44 @@ using OrchardCore.Flows.Models; using OrchardCore.Flows.ViewModels; -namespace OrchardCore.Flows.Settings +namespace OrchardCore.Flows.Settings; + +public sealed class FlowPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class FlowPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + + public FlowPartSettingsDisplayDriver(IContentDefinitionManager contentDefinitionManager) { - private readonly IContentDefinitionManager _contentDefinitionManager; + _contentDefinitionManager = contentDefinitionManager; + } - public FlowPartSettingsDisplayDriver(IContentDefinitionManager contentDefinitionManager) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("FlowPartSettings_Edit", async model => { - _contentDefinitionManager = contentDefinitionManager; - } + model.FlowPartSettings = contentTypePartDefinition.GetSettings(); + model.ContainedContentTypes = model.FlowPartSettings.ContainedContentTypes; + model.ContentTypes = []; - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) - { - return Initialize("FlowPartSettings_Edit", async model => + foreach (var contentTypeDefinition in (await _contentDefinitionManager.ListTypeDefinitionsAsync()).Where(t => t.GetStereotype() == "Widget")) { - model.FlowPartSettings = contentTypePartDefinition.GetSettings(); - model.ContainedContentTypes = model.FlowPartSettings.ContainedContentTypes; - model.ContentTypes = []; - - foreach (var contentTypeDefinition in (await _contentDefinitionManager.ListTypeDefinitionsAsync()).Where(t => t.GetStereotype() == "Widget")) - { - model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); - } - }).Location("Content"); - } - - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new FlowPartSettingsViewModel(); + model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); + } + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.ContainedContentTypes); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new FlowPartSettingsViewModel(); - context.Builder.WithSettings(new FlowPartSettings - { - ContainedContentTypes = model.ContainedContentTypes - }); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.ContainedContentTypes); + + context.Builder.WithSettings(new FlowPartSettings + { + ContainedContentTypes = model.ContainedContentTypes + }); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Flows/Startup.cs index 2b6797f6ca5..4b2b727b07f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/Startup.cs @@ -15,40 +15,39 @@ using OrchardCore.Modules; using OrchardCore.ResourceManagement; -namespace OrchardCore.Flows +namespace OrchardCore.Flows; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - - // Flow Part - services.AddContentPart() - .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); - - services.AddScoped(); - - // Bag Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); - services.AddScoped(); - services.AddScoped(); - - services.AddContentPart(); - - services.AddDataMigration(); - - services.AddTransient, ResourceManagementOptionsConfiguration>(); - } + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); + + // Flow Part + services.AddContentPart() + .UseDisplayDriver(); + services.AddScoped(); + services.AddScoped(); + + services.AddScoped(); + + // Bag Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); + services.AddScoped(); + services.AddScoped(); + + services.AddContentPart(); + + services.AddDataMigration(); + + services.AddTransient, ResourceManagementOptionsConfiguration>(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartEditViewModel.cs index f58f0713cbe..9e37b7110ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartEditViewModel.cs @@ -5,28 +5,27 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class BagPartEditViewModel { - public class BagPartEditViewModel - { - public string[] Prefixes { get; set; } = []; - public string[] ContentTypes { get; set; } = []; - public string[] ContentItems { get; set; } = []; + public string[] Prefixes { get; set; } = []; + public string[] ContentTypes { get; set; } = []; + public string[] ContentItems { get; set; } = []; - [BindNever] - public BagPart BagPart { get; set; } + [BindNever] + public BagPart BagPart { get; set; } - [IgnoreDataMember] - [BindNever] - public IUpdateModel Updater { get; set; } + [IgnoreDataMember] + [BindNever] + public IUpdateModel Updater { get; set; } - [BindNever] - public IEnumerable ContainedContentTypeDefinitions { get; set; } + [BindNever] + public IEnumerable ContainedContentTypeDefinitions { get; set; } - [BindNever] - public IEnumerable AccessibleWidgets { get; set; } + [BindNever] + public IEnumerable AccessibleWidgets { get; set; } - [BindNever] - public ContentTypePartDefinition TypePartDefinition { get; set; } - } + [BindNever] + public ContentTypePartDefinition TypePartDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartSettingsViewModel.cs index 6fcff35569c..9701a5ed655 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartSettingsViewModel.cs @@ -1,15 +1,14 @@ using System.Collections.Specialized; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class BagPartSettingsViewModel { - public class BagPartSettingsViewModel - { - public BagPartSettings BagPartSettings { get; set; } - public NameValueCollection ContentTypes { get; set; } - public string DisplayType { get; set; } - public string[] ContainedContentTypes { get; set; } = []; - public BagPartSettingType Source { get; set; } - public string Stereotypes { get; set; } - } + public BagPartSettings BagPartSettings { get; set; } + public NameValueCollection ContentTypes { get; set; } + public string DisplayType { get; set; } + public string[] ContainedContentTypes { get; set; } = []; + public BagPartSettingType Source { get; set; } + public string Stereotypes { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartViewModel.cs index f17ea23beab..c3d89e60d94 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartViewModel.cs @@ -5,18 +5,17 @@ using OrchardCore.ContentManagement.Display.Models; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class BagPartViewModel { - public class BagPartViewModel - { - public BagPart BagPart { get; set; } - public IEnumerable ContentItems => BagPart.ContentItems; + public BagPart BagPart { get; set; } + public IEnumerable ContentItems => BagPart.ContentItems; - [IgnoreDataMember] - [BindNever] - public BuildPartDisplayContext BuildPartDisplayContext { get; set; } + [IgnoreDataMember] + [BindNever] + public BuildPartDisplayContext BuildPartDisplayContext { get; set; } - public BagPartSettings Settings { get; set; } - public string DisplayType => Settings?.DisplayType; - } + public BagPartSettings Settings { get; set; } + public string DisplayType => Settings?.DisplayType; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartWidgetViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartWidgetViewModel.cs index c0c425c096d..839b5ae37da 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartWidgetViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BagPartWidgetViewModel.cs @@ -1,18 +1,17 @@ using OrchardCore.ContentManagement; using OrchardCore.ContentManagement.Metadata.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class BagPartWidgetViewModel { - public class BagPartWidgetViewModel - { - public ContentItem ContentItem { get; set; } + public ContentItem ContentItem { get; set; } - public ContentTypeDefinition ContentTypeDefinition { get; set; } + public ContentTypeDefinition ContentTypeDefinition { get; set; } - public bool Editable { get; set; } + public bool Editable { get; set; } - public bool Viewable { get; set; } + public bool Viewable { get; set; } - public bool Deletable { get; set; } - } + public bool Deletable { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BuildEditorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BuildEditorViewModel.cs index abf602e83d1..8167d7b56a4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BuildEditorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/BuildEditorViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class BuildEditorViewModel { - public class BuildEditorViewModel - { - public dynamic EditorShape { get; set; } - } + public dynamic EditorShape { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartEditViewModel.cs index 89f4d95507d..cdf220dbc6e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartEditViewModel.cs @@ -5,27 +5,26 @@ using OrchardCore.DisplayManagement.ModelBinding; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class FlowPartEditViewModel { - public class FlowPartEditViewModel - { - // Each element in a FlowPart is projected to the Prefixes and ContentTypes arrays - // If for instance a FlowPart has three content items, there will be three elements in both - // Prefixes and ContentTypes. Each value in Prefixes is a Guid that represents the unique - // HtmlFieldPrefix value of its editor. + // Each element in a FlowPart is projected to the Prefixes and ContentTypes arrays + // If for instance a FlowPart has three content items, there will be three elements in both + // Prefixes and ContentTypes. Each value in Prefixes is a Guid that represents the unique + // HtmlFieldPrefix value of its editor. - public string[] Prefixes { get; set; } = []; - public string[] ContentTypes { get; set; } = []; - public string[] ContentItems { get; set; } = []; + public string[] Prefixes { get; set; } = []; + public string[] ContentTypes { get; set; } = []; + public string[] ContentItems { get; set; } = []; - [BindNever] - public FlowPart FlowPart { get; set; } + [BindNever] + public FlowPart FlowPart { get; set; } - [IgnoreDataMember] - [BindNever] - public IUpdateModel Updater { get; set; } + [IgnoreDataMember] + [BindNever] + public IUpdateModel Updater { get; set; } - [BindNever] - public IEnumerable ContainedContentTypeDefinitions { get; set; } - } + [BindNever] + public IEnumerable ContainedContentTypeDefinitions { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartSettingsViewModel.cs index a3c7beadd06..e0497870dcf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartSettingsViewModel.cs @@ -1,12 +1,11 @@ using System.Collections.Specialized; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class FlowPartSettingsViewModel { - public class FlowPartSettingsViewModel - { - public FlowPartSettings FlowPartSettings { get; set; } - public NameValueCollection ContentTypes { get; set; } - public string[] ContainedContentTypes { get; set; } = []; - } + public FlowPartSettings FlowPartSettings { get; set; } + public NameValueCollection ContentTypes { get; set; } + public string[] ContainedContentTypes { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartViewModel.cs index 449e39fe669..ea13b2a1f6b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Flows/ViewModels/FlowPartViewModel.cs @@ -3,14 +3,13 @@ using OrchardCore.ContentManagement.Display.Models; using OrchardCore.Flows.Models; -namespace OrchardCore.Flows.ViewModels +namespace OrchardCore.Flows.ViewModels; + +public class FlowPartViewModel { - public class FlowPartViewModel - { - public FlowPart FlowPart { get; set; } + public FlowPart FlowPart { get; set; } - [IgnoreDataMember] - [BindNever] - public BuildPartDisplayContext BuildPartDisplayContext { get; set; } - } + [IgnoreDataMember] + [BindNever] + public BuildPartDisplayContext BuildPartDisplayContext { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ButtonPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ButtonPartDisplayDriver.cs index 1028f8a21fb..4c056e281fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ButtonPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ButtonPartDisplayDriver.cs @@ -6,35 +6,34 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class ButtonPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ButtonPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ButtonPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(ButtonPart part, BuildPartDisplayContext context) - { - return View("ButtonPart", part) - .Location("Detail", "Content"); - } + return View("ButtonPart", part) + .Location("Detail", "Content"); + } - public override IDisplayResult Edit(ButtonPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(ButtonPart part, BuildPartEditorContext context) + { + return Initialize("ButtonPart_Fields_Edit", m => { - return Initialize("ButtonPart_Fields_Edit", m => - { - m.Text = part.Text; - m.Type = part.Type; - }); - } + m.Text = part.Text; + m.Type = part.Type; + }); + } - public async override Task UpdateAsync(ButtonPart part, UpdatePartEditorContext context) - { - var viewModel = new ButtonPartEditViewModel(); + public async override Task UpdateAsync(ButtonPart part, UpdatePartEditorContext context) + { + var viewModel = new ButtonPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.Text = viewModel.Text?.Trim(); - part.Type = viewModel.Type?.Trim(); + part.Text = viewModel.Text?.Trim(); + part.Type = viewModel.Type?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormContentDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormContentDisplayDriver.cs index b4644016298..19666b398c1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormContentDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormContentDisplayDriver.cs @@ -5,23 +5,22 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class FormContentDisplayDriver : ContentDisplayDriver { - public sealed class FormContentDisplayDriver : ContentDisplayDriver + public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) { - public override Task DisplayAsync(ContentItem model, BuildDisplayContext context) + var formItemShape = context.Shape; + // If the content item contains FormPart add Form Wrapper only in Display type Detail + var formPart = model.As(); + if (formPart != null && context.DisplayType == "Detail") { - var formItemShape = context.Shape; - // If the content item contains FormPart add Form Wrapper only in Display type Detail - var formPart = model.As(); - if (formPart != null && context.DisplayType == "Detail") - { - // Add wrapper for content type if template is not available it will fall back to Form_Wrapper - formItemShape.Metadata.Wrappers.Add($"Form_Wrapper__{model.ContentType}"); - } - - // We don't need to return a shape result - return Task.FromResult(null); + // Add wrapper for content type if template is not available it will fall back to Form_Wrapper + formItemShape.Metadata.Wrappers.Add($"Form_Wrapper__{model.ContentType}"); } + + // We don't need to return a shape result + return Task.FromResult(null); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormElementPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormElementPartDisplayDriver.cs index 4c0dacd30ab..25bbaeac495 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormElementPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormElementPartDisplayDriver.cs @@ -6,27 +6,26 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class FormElementPartDisplayDriver : ContentPartDisplayDriver { - public sealed class FormElementPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Edit(FormElementPart part, BuildPartEditorContext context) { - public override IDisplayResult Edit(FormElementPart part, BuildPartEditorContext context) + return Initialize("FormElementPart_Fields_Edit", m => { - return Initialize("FormElementPart_Fields_Edit", m => - { - m.Id = part.Id; - }); - } + m.Id = part.Id; + }); + } - public async override Task UpdateAsync(FormElementPart part, UpdatePartEditorContext context) - { - var viewModel = new FormElementPartEditViewModel(); + public async override Task UpdateAsync(FormElementPart part, UpdatePartEditorContext context) + { + var viewModel = new FormElementPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.Id = viewModel.Id?.Trim(); + part.Id = viewModel.Id?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs index 79ab960a471..e13f3ef31af 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormInputElementPartDisplayDriver.cs @@ -7,40 +7,39 @@ using OrchardCore.Forms.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class FormInputElementPartDisplayDriver : ContentPartDisplayDriver { - public sealed class FormInputElementPartDisplayDriver : ContentPartDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public FormInputElementPartDisplayDriver(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public FormInputElementPartDisplayDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public override IDisplayResult Edit(FormInputElementPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(FormInputElementPart part, BuildPartEditorContext context) + { + return Initialize("FormInputElementPart_Fields_Edit", m => { - return Initialize("FormInputElementPart_Fields_Edit", m => - { - m.Name = part.Name; - }); - } + m.Name = part.Name; + }); + } - public async override Task UpdateAsync(FormInputElementPart part, UpdatePartEditorContext context) - { - var viewModel = new FormInputElementPartEditViewModel(); + public async override Task UpdateAsync(FormInputElementPart part, UpdatePartEditorContext context) + { + var viewModel = new FormInputElementPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - if (string.IsNullOrWhiteSpace(viewModel.Name)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Name), S["A value is required for Name."]); - } + if (string.IsNullOrWhiteSpace(viewModel.Name)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Name), S["A value is required for Name."]); + } - part.Name = viewModel.Name?.Trim(); - part.ContentItem.DisplayText = part.Name; + part.Name = viewModel.Name?.Trim(); + part.ContentItem.DisplayText = part.Name; - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormPartDisplayDriver.cs index 949981509b3..904ee3649d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/FormPartDisplayDriver.cs @@ -5,37 +5,36 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class FormPartDisplayDriver : ContentPartDisplayDriver { - public sealed class FormPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Edit(FormPart part, BuildPartEditorContext context) { - public override IDisplayResult Edit(FormPart part, BuildPartEditorContext context) + return Initialize("FormPart_Fields_Edit", m => { - return Initialize("FormPart_Fields_Edit", m => - { - m.Action = part.Action; - m.Method = part.Method; - m.WorkflowTypeId = part.WorkflowTypeId; - m.EncType = part.EncType; - m.EnableAntiForgeryToken = part.EnableAntiForgeryToken; - m.SaveFormLocation = part.SaveFormLocation; - }); - } + m.Action = part.Action; + m.Method = part.Method; + m.WorkflowTypeId = part.WorkflowTypeId; + m.EncType = part.EncType; + m.EnableAntiForgeryToken = part.EnableAntiForgeryToken; + m.SaveFormLocation = part.SaveFormLocation; + }); + } - public async override Task UpdateAsync(FormPart part, UpdatePartEditorContext context) - { - var viewModel = new FormPartEditViewModel(); + public async override Task UpdateAsync(FormPart part, UpdatePartEditorContext context) + { + var viewModel = new FormPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.Action = viewModel.Action?.Trim(); - part.Method = viewModel.Method; - part.WorkflowTypeId = viewModel.WorkflowTypeId; - part.EncType = viewModel.EncType; - part.EnableAntiForgeryToken = viewModel.EnableAntiForgeryToken; - part.SaveFormLocation = viewModel.SaveFormLocation; + part.Action = viewModel.Action?.Trim(); + part.Method = viewModel.Method; + part.WorkflowTypeId = viewModel.WorkflowTypeId; + part.EncType = viewModel.EncType; + part.EnableAntiForgeryToken = viewModel.EnableAntiForgeryToken; + part.SaveFormLocation = viewModel.SaveFormLocation; - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/InputPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/InputPartDisplayDriver.cs index 02d91c0a446..3b533e254d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/InputPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/InputPartDisplayDriver.cs @@ -6,36 +6,35 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class InputPartDisplayDriver : ContentPartDisplayDriver { - public sealed class InputPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(InputPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(InputPart part, BuildPartDisplayContext context) - { - return View("InputPart", part).Location("Detail", "Content"); - } + return View("InputPart", part).Location("Detail", "Content"); + } - public override IDisplayResult Edit(InputPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(InputPart part, BuildPartEditorContext context) + { + return Initialize("InputPart_Fields_Edit", m => { - return Initialize("InputPart_Fields_Edit", m => - { - m.Placeholder = part.Placeholder; - m.DefaultValue = part.DefaultValue; - m.Type = part.Type; - }); - } + m.Placeholder = part.Placeholder; + m.DefaultValue = part.DefaultValue; + m.Type = part.Type; + }); + } - public async override Task UpdateAsync(InputPart part, UpdatePartEditorContext context) - { - var viewModel = new InputPartEditViewModel(); + public async override Task UpdateAsync(InputPart part, UpdatePartEditorContext context) + { + var viewModel = new InputPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.Placeholder = viewModel.Placeholder?.Trim(); - part.DefaultValue = viewModel.DefaultValue?.Trim(); - part.Type = viewModel.Type?.Trim(); + part.Placeholder = viewModel.Placeholder?.Trim(); + part.DefaultValue = viewModel.DefaultValue?.Trim(); + part.Type = viewModel.Type?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/LabelPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/LabelPartDisplayDriver.cs index 142c1b94d6a..562938c04d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/LabelPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/LabelPartDisplayDriver.cs @@ -6,32 +6,31 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class LabelPartDisplayDriver : ContentPartDisplayDriver { - public sealed class LabelPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(LabelPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(LabelPart part, BuildPartDisplayContext context) - { - return View("LabelPart", part).Location("Detail", "Content"); - } + return View("LabelPart", part).Location("Detail", "Content"); + } - public override IDisplayResult Edit(LabelPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(LabelPart part, BuildPartEditorContext context) + { + return Initialize("LabelPart_Fields_Edit", m => { - return Initialize("LabelPart_Fields_Edit", m => - { - m.For = part.For; - }); - } + m.For = part.For; + }); + } - public override async Task UpdateAsync(LabelPart part, UpdatePartEditorContext context) - { - var viewModel = new LabelPartEditViewModel(); + public override async Task UpdateAsync(LabelPart part, UpdatePartEditorContext context) + { + var viewModel = new LabelPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.For = viewModel.For?.Trim(); + part.For = viewModel.For?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/SelectPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/SelectPartDisplayDriver.cs index 2c2092a4b84..41ec37caa51 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/SelectPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/SelectPartDisplayDriver.cs @@ -8,51 +8,50 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class SelectPartDisplayDriver : ContentPartDisplayDriver { - public sealed class SelectPartDisplayDriver : ContentPartDisplayDriver + internal readonly IStringLocalizer S; + + public SelectPartDisplayDriver(IStringLocalizer stringLocalizer) { - internal readonly IStringLocalizer S; + S = stringLocalizer; + } - public SelectPartDisplayDriver(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public override IDisplayResult Display(SelectPart part, BuildPartDisplayContext context) + { + return View("SelectPart", part).Location("Detail", "Content"); + } - public override IDisplayResult Display(SelectPart part, BuildPartDisplayContext context) + public override IDisplayResult Edit(SelectPart part, BuildPartEditorContext context) + { + return Initialize("SelectPart_Fields_Edit", m => { - return View("SelectPart", part).Location("Detail", "Content"); - } + m.Options = JConvert.SerializeObject(part.Options ?? [], JOptions.CamelCaseIndented); + m.DefaultValue = part.DefaultValue; + m.Editor = part.Editor; + }); + } - public override IDisplayResult Edit(SelectPart part, BuildPartEditorContext context) + public async override Task UpdateAsync(SelectPart part, UpdatePartEditorContext context) + { + var viewModel = new SelectPartEditViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + part.DefaultValue = viewModel.DefaultValue; + + try { - return Initialize("SelectPart_Fields_Edit", m => - { - m.Options = JConvert.SerializeObject(part.Options ?? [], JOptions.CamelCaseIndented); - m.DefaultValue = part.DefaultValue; - m.Editor = part.Editor; - }); + part.Editor = viewModel.Editor; + part.Options = string.IsNullOrWhiteSpace(viewModel.Options) + ? [] + : JConvert.DeserializeObject(viewModel.Options); } - - public async override Task UpdateAsync(SelectPart part, UpdatePartEditorContext context) + catch { - var viewModel = new SelectPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.DefaultValue = viewModel.DefaultValue; - - try - { - part.Editor = viewModel.Editor; - part.Options = string.IsNullOrWhiteSpace(viewModel.Options) - ? [] - : JConvert.DeserializeObject(viewModel.Options); - } - catch - { - context.Updater.ModelState.AddModelError(Prefix + '.' + nameof(SelectPartEditViewModel.Options), S["The options are written in an incorrect format."]); - } - - return Edit(part, context); + context.Updater.ModelState.AddModelError(Prefix + '.' + nameof(SelectPartEditViewModel.Options), S["The options are written in an incorrect format."]); } + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/TextAreaPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/TextAreaPartDisplayDriver.cs index 8d15e60df88..995b1e01ef5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/TextAreaPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/TextAreaPartDisplayDriver.cs @@ -5,34 +5,33 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class TextAreaPartDisplayDriver : ContentPartDisplayDriver { - public sealed class TextAreaPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(TextAreaPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(TextAreaPart part, BuildPartDisplayContext context) - { - return View("TextAreaPart", part).Location("Detail", "Content"); - } + return View("TextAreaPart", part).Location("Detail", "Content"); + } - public override IDisplayResult Edit(TextAreaPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(TextAreaPart part, BuildPartEditorContext context) + { + return Initialize("TextAreaPart_Fields_Edit", m => { - return Initialize("TextAreaPart_Fields_Edit", m => - { - m.Placeholder = part.Placeholder; - m.DefaultValue = part.DefaultValue; - }); - } + m.Placeholder = part.Placeholder; + m.DefaultValue = part.DefaultValue; + }); + } - public async override Task UpdateAsync(TextAreaPart part, UpdatePartEditorContext context) - { - var viewModel = new InputPartEditViewModel(); + public async override Task UpdateAsync(TextAreaPart part, UpdatePartEditorContext context) + { + var viewModel = new InputPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.Placeholder = viewModel.Placeholder?.Trim(); - part.DefaultValue = viewModel.DefaultValue?.Trim(); + part.Placeholder = viewModel.Placeholder?.Trim(); + part.DefaultValue = viewModel.DefaultValue?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationPartDisplayDriver.cs index 7730b38bfee..b1c05efc32a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationPartDisplayDriver.cs @@ -5,32 +5,31 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class ValidationPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ValidationPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ValidationPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(ValidationPart part, BuildPartDisplayContext context) - { - return View("ValidationPart", part).Location("Detail", "Content"); - } + return View("ValidationPart", part).Location("Detail", "Content"); + } - public override IDisplayResult Edit(ValidationPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(ValidationPart part, BuildPartEditorContext context) + { + return Initialize("ValidationPart_Fields_Edit", m => { - return Initialize("ValidationPart_Fields_Edit", m => - { - m.For = part.For; - }); - } + m.For = part.For; + }); + } - public async override Task UpdateAsync(ValidationPart part, UpdatePartEditorContext context) - { - var viewModel = new ValidationPartEditViewModel(); + public async override Task UpdateAsync(ValidationPart part, UpdatePartEditorContext context) + { + var viewModel = new ValidationPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - part.For = viewModel.For?.Trim(); + part.For = viewModel.For?.Trim(); - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationSummaryPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationSummaryPartDisplayDriver.cs index dbb75ababf6..538b228d6f2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationSummaryPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Drivers/ValidationSummaryPartDisplayDriver.cs @@ -5,33 +5,32 @@ using OrchardCore.Forms.Models; using OrchardCore.Forms.ViewModels; -namespace OrchardCore.Forms.Drivers +namespace OrchardCore.Forms.Drivers; + +public sealed class ValidationSummaryPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ValidationSummaryPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ValidationSummaryPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(ValidationSummaryPart part, BuildPartDisplayContext context) - { - return View("ValidationSummaryPart", part) - .Location("Detail", "Content"); - } + return View("ValidationSummaryPart", part) + .Location("Detail", "Content"); + } - public override IDisplayResult Edit(ValidationSummaryPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(ValidationSummaryPart part, BuildPartEditorContext context) + { + return Initialize("ValidationSummaryPart_Fields_Edit", model => { - return Initialize("ValidationSummaryPart_Fields_Edit", model => - { - model.ModelOnly = part.ModelOnly; - }); - } + model.ModelOnly = part.ModelOnly; + }); + } - public override async Task UpdateAsync(ValidationSummaryPart part, UpdatePartEditorContext context) - { - var model = new ValidationSummaryViewModel(); + public override async Task UpdateAsync(ValidationSummaryPart part, UpdatePartEditorContext context) + { + var model = new ValidationSummaryViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - part.ModelOnly = model.ModelOnly; + part.ModelOnly = model.ModelOnly; - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ExportModelStateAttribute.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ExportModelStateAttribute.cs index 57983d8fafb..b9e200b8a93 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ExportModelStateAttribute.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ExportModelStateAttribute.cs @@ -3,37 +3,36 @@ using Microsoft.AspNetCore.Mvc.Filters; using OrchardCore.Forms.Helpers; -namespace OrchardCore.Forms.Filters +namespace OrchardCore.Forms.Filters; + +public sealed class ExportModelStateAttribute : ModelStateTransferAttribute { - public sealed class ExportModelStateAttribute : ModelStateTransferAttribute + public override void OnActionExecuted(ActionExecutedContext context) { - public override void OnActionExecuted(ActionExecutedContext context) + // Only export if ModelState is not valid. + if (context.ModelState != null && !context.ModelState.IsValid && IsRedirect(context)) { - // Only export if ModelState is not valid. - if (context.ModelState != null && !context.ModelState.IsValid && IsRedirect(context)) + var controller = context.Controller as Controller; + if (controller != null) { - var controller = context.Controller as Controller; - if (controller != null) - { - controller.TempData[Key] = ModelStateHelpers.SerializeModelState(context.ModelState); - } + controller.TempData[Key] = ModelStateHelpers.SerializeModelState(context.ModelState); } - - base.OnActionExecuted(context); } - private static bool IsRedirect(ActionExecutedContext context) - { - var result = context.Result; - var statusCode = context.HttpContext.Response.StatusCode; + base.OnActionExecuted(context); + } - return - result is RedirectResult || - result is RedirectToRouteResult || - result is RedirectToActionResult || - result is LocalRedirectResult || - statusCode == (int)HttpStatusCode.Redirect || - statusCode == (int)HttpStatusCode.TemporaryRedirect; - } + private static bool IsRedirect(ActionExecutedContext context) + { + var result = context.Result; + var statusCode = context.HttpContext.Response.StatusCode; + + return + result is RedirectResult || + result is RedirectToRouteResult || + result is RedirectToActionResult || + result is LocalRedirectResult || + statusCode == (int)HttpStatusCode.Redirect || + statusCode == (int)HttpStatusCode.TemporaryRedirect; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStateAttribute.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStateAttribute.cs index 4cd64e4171d..64b55178b58 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStateAttribute.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStateAttribute.cs @@ -3,31 +3,30 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using OrchardCore.Forms.Helpers; -namespace OrchardCore.Forms.Filters +namespace OrchardCore.Forms.Filters; + +public sealed class ImportModelStateAttribute : ModelStateTransferAttribute { - public sealed class ImportModelStateAttribute : ModelStateTransferAttribute + public override void OnActionExecuted(ActionExecutedContext context) { - public override void OnActionExecuted(ActionExecutedContext context) - { - var controller = context.Controller as Controller; - var serializedModelState = controller?.TempData[Key] as string; + var controller = context.Controller as Controller; + var serializedModelState = controller?.TempData[Key] as string; - if (serializedModelState != null) + if (serializedModelState != null) + { + // Only Import if we are viewing. + if (context.Result is ViewResult || context.Result is PageResult) { - // Only Import if we are viewing. - if (context.Result is ViewResult || context.Result is PageResult) - { - var modelState = ModelStateHelpers.DeserializeModelState(serializedModelState); - context.ModelState.Merge(modelState); - } - else - { - // Otherwise remove it. - controller.TempData.Remove(Key); - } + var modelState = ModelStateHelpers.DeserializeModelState(serializedModelState); + context.ModelState.Merge(modelState); + } + else + { + // Otherwise remove it. + controller.TempData.Remove(Key); } - - base.OnActionExecuted(context); } + + base.OnActionExecuted(context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStatePageFilter.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStatePageFilter.cs index a540db79cdb..f99a41a398b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStatePageFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ImportModelStatePageFilter.cs @@ -2,39 +2,38 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using OrchardCore.Forms.Helpers; -namespace OrchardCore.Forms.Filters +namespace OrchardCore.Forms.Filters; + +// For Razor Pages IActionFilter\ActionFilterAttribute don't apply, IPageFilter have to be used instead. +public sealed class ImportModelStatePageFilter : IPageFilter { - // For Razor Pages IActionFilter\ActionFilterAttribute don't apply, IPageFilter have to be used instead. - public sealed class ImportModelStatePageFilter : IPageFilter + public void OnPageHandlerExecuted(PageHandlerExecutedContext context) { - public void OnPageHandlerExecuted(PageHandlerExecutedContext context) - { - var pageModel = context.HandlerInstance as PageModel; + var pageModel = context.HandlerInstance as PageModel; - var serializedModelState = pageModel?.TempData[ModelStateTransferAttribute.Key] as string; + var serializedModelState = pageModel?.TempData[ModelStateTransferAttribute.Key] as string; - if (serializedModelState != null) + if (serializedModelState != null) + { + // Only Import if we are viewing. + if (context.Result is PageResult) { - // Only Import if we are viewing. - if (context.Result is PageResult) - { - var modelState = ModelStateHelpers.DeserializeModelState(serializedModelState); - context.ModelState.Merge(modelState); - } - else - { - // Otherwise remove it. - pageModel.TempData.Remove(ModelStateTransferAttribute.Key); - } + var modelState = ModelStateHelpers.DeserializeModelState(serializedModelState); + context.ModelState.Merge(modelState); + } + else + { + // Otherwise remove it. + pageModel.TempData.Remove(ModelStateTransferAttribute.Key); } } + } - public void OnPageHandlerExecuting(PageHandlerExecutingContext context) - { - } + public void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + } - public void OnPageHandlerSelected(PageHandlerSelectedContext context) - { - } + public void OnPageHandlerSelected(PageHandlerSelectedContext context) + { } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ModelStateTransferAttribute.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ModelStateTransferAttribute.cs index a2ed65e475e..3f16c5d0636 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ModelStateTransferAttribute.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Filters/ModelStateTransferAttribute.cs @@ -1,9 +1,8 @@ using Microsoft.AspNetCore.Mvc.Filters; -namespace OrchardCore.Forms.Filters +namespace OrchardCore.Forms.Filters; + +public abstract class ModelStateTransferAttribute : ActionFilterAttribute { - public abstract class ModelStateTransferAttribute : ActionFilterAttribute - { - internal const string Key = nameof(ModelStateTransferAttribute); - } + internal const string Key = nameof(ModelStateTransferAttribute); } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ButtonPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ButtonPartQueryObjectType.cs index f48d8674fbc..bf3edeead84 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ButtonPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ButtonPartQueryObjectType.cs @@ -1,16 +1,15 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class ButtonPartQueryObjectType : ObjectGraphType { - public class ButtonPartQueryObjectType : ObjectGraphType + public ButtonPartQueryObjectType() { - public ButtonPartQueryObjectType() - { - Name = "ButtonPart"; + Name = "ButtonPart"; - Field(x => x.Text, nullable: true); - Field(x => x.Type, nullable: true); - } + Field(x => x.Text, nullable: true); + Field(x => x.Type, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormElementPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormElementPartQueryObjectType.cs index d84f8e5080b..9afd5896365 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormElementPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormElementPartQueryObjectType.cs @@ -1,15 +1,14 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class FormElementPartQueryObjectType : ObjectGraphType { - public class FormElementPartQueryObjectType : ObjectGraphType + public FormElementPartQueryObjectType() { - public FormElementPartQueryObjectType() - { - Name = "FormElementPart"; + Name = "FormElementPart"; - Field(x => x.Id, nullable: true); - } + Field(x => x.Id, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormInputElementPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormInputElementPartQueryObjectType.cs index 17fefe699f3..e4caf86d6aa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormInputElementPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormInputElementPartQueryObjectType.cs @@ -1,15 +1,14 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class FormInputElementPartQueryObjectType : ObjectGraphType { - public class FormInputElementPartQueryObjectType : ObjectGraphType + public FormInputElementPartQueryObjectType() { - public FormInputElementPartQueryObjectType() - { - Name = "FormInputElementPart"; + Name = "FormInputElementPart"; - Field(x => x.Name, nullable: true); - } + Field(x => x.Name, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormPartQueryObjectType.cs index 5953bcc612a..b6cb8da7115 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/FormPartQueryObjectType.cs @@ -1,17 +1,16 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class FormPartQueryObjectType : ObjectGraphType { - public class FormPartQueryObjectType : ObjectGraphType + public FormPartQueryObjectType() { - public FormPartQueryObjectType() - { - Name = "FormPart"; + Name = "FormPart"; - Field(x => x.WorkflowTypeId, nullable: true); - Field(x => x.Action, nullable: true); - Field(x => x.Method, nullable: true); - } + Field(x => x.WorkflowTypeId, nullable: true); + Field(x => x.Action, nullable: true); + Field(x => x.Method, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/InputPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/InputPartQueryObjectType.cs index 0c5e98c624b..553f439ac7a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/InputPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/InputPartQueryObjectType.cs @@ -1,17 +1,16 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class InputPartQueryObjectType : ObjectGraphType { - public class InputPartQueryObjectType : ObjectGraphType + public InputPartQueryObjectType() { - public InputPartQueryObjectType() - { - Name = "InputPart"; + Name = "InputPart"; - Field(x => x.Type, nullable: true); - Field(x => x.Placeholder, nullable: true); - Field(x => x.DefaultValue, nullable: true); - } + Field(x => x.Type, nullable: true); + Field(x => x.Placeholder, nullable: true); + Field(x => x.DefaultValue, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/LabelPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/LabelPartQueryObjectType.cs index a034917d3d9..edea8c0e74c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/LabelPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/LabelPartQueryObjectType.cs @@ -1,16 +1,15 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class LabelPartQueryObjectType : ObjectGraphType { - public class LabelPartQueryObjectType : ObjectGraphType + public LabelPartQueryObjectType() { - public LabelPartQueryObjectType() - { - Name = "LabelPart"; + Name = "LabelPart"; - Field(x => x.For, nullable: true); - Field("value", context => context.ContentItem.DisplayText); - } + Field(x => x.For, nullable: true); + Field("value", context => context.ContentItem.DisplayText); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/Startup.cs index 27ce72adc3c..0d89f436dfe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/Startup.cs @@ -3,24 +3,23 @@ using OrchardCore.Forms.Models; using OrchardCore.Modules; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); - // Broken - // services.AddGraphQLQueryType(); - } + // Broken + // services.AddGraphQLQueryType(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/TextAreaPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/TextAreaPartQueryObjectType.cs index 65a7b996cea..88ca58e61bf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/TextAreaPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/TextAreaPartQueryObjectType.cs @@ -1,16 +1,15 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class TextAreaPartQueryObjectType : ObjectGraphType { - public class TextAreaPartQueryObjectType : ObjectGraphType + public TextAreaPartQueryObjectType() { - public TextAreaPartQueryObjectType() - { - Name = "TextAreaPart"; + Name = "TextAreaPart"; - Field(x => x.DefaultValue, nullable: true); - Field(x => x.Placeholder, nullable: true); - } + Field(x => x.DefaultValue, nullable: true); + Field(x => x.Placeholder, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationPartQueryObjectType.cs index 4172228e94a..aa489a37056 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationPartQueryObjectType.cs @@ -1,15 +1,14 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class ValidationPartQueryObjectType : ObjectGraphType { - public class ValidationPartQueryObjectType : ObjectGraphType + public ValidationPartQueryObjectType() { - public ValidationPartQueryObjectType() - { - Name = "ValidationPart"; + Name = "ValidationPart"; - Field(x => x.For, nullable: true); - } + Field(x => x.For, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationSummaryPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationSummaryPartQueryObjectType.cs index 589fae3a92f..8da51ce951a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationSummaryPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/GraphQL/ValidationSummaryPartQueryObjectType.cs @@ -1,13 +1,12 @@ using GraphQL.Types; using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.GraphQL +namespace OrchardCore.Forms.GraphQL; + +public class ValidationSummaryPartQueryObjectType : ObjectGraphType { - public class ValidationSummaryPartQueryObjectType : ObjectGraphType + public ValidationSummaryPartQueryObjectType() { - public ValidationSummaryPartQueryObjectType() - { - Name = "ValidationSummaryPart"; - } + Name = "ValidationSummaryPart"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Helpers/ModelStateHelpers.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Helpers/ModelStateHelpers.cs index 37e22297109..16f3cfb8343 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Helpers/ModelStateHelpers.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Helpers/ModelStateHelpers.cs @@ -4,46 +4,45 @@ using System.Text.Json.Nodes; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Forms.Helpers +namespace OrchardCore.Forms.Helpers; + +public static class ModelStateHelpers { - public static class ModelStateHelpers + public static string SerializeModelState(ModelStateDictionary modelState) { - public static string SerializeModelState(ModelStateDictionary modelState) + var errorList = modelState.Select(x => new ModelStateTransferValue { - var errorList = modelState.Select(x => new ModelStateTransferValue - { - Key = x.Key, - AttemptedValue = x.Value.AttemptedValue, - RawValue = x.Value.RawValue, - ErrorMessages = x.Value.Errors.Select(err => err.ErrorMessage).ToList(), - }); + Key = x.Key, + AttemptedValue = x.Value.AttemptedValue, + RawValue = x.Value.RawValue, + ErrorMessages = x.Value.Errors.Select(err => err.ErrorMessage).ToList(), + }); - return JConvert.SerializeObject(errorList); - } + return JConvert.SerializeObject(errorList); + } - public static ModelStateDictionary DeserializeModelState(string serialisedErrorList) - { - var errorList = JConvert.DeserializeObject>(serialisedErrorList); - var modelState = new ModelStateDictionary(); + public static ModelStateDictionary DeserializeModelState(string serialisedErrorList) + { + var errorList = JConvert.DeserializeObject>(serialisedErrorList); + var modelState = new ModelStateDictionary(); - foreach (var item in errorList) + foreach (var item in errorList) + { + item.RawValue = item.RawValue is JsonArray jarray ? jarray.ToObject() : item.RawValue; + modelState.SetModelValue(item.Key, item.RawValue, item.AttemptedValue); + foreach (var error in item.ErrorMessages) { - item.RawValue = item.RawValue is JsonArray jarray ? jarray.ToObject() : item.RawValue; - modelState.SetModelValue(item.Key, item.RawValue, item.AttemptedValue); - foreach (var error in item.ErrorMessages) - { - modelState.AddModelError(item.Key, error); - } + modelState.AddModelError(item.Key, error); } - return modelState; } + return modelState; + } - private sealed class ModelStateTransferValue - { - public string Key { get; set; } - public string AttemptedValue { get; set; } - public object RawValue { get; set; } - public ICollection ErrorMessages { get; set; } = []; - } + private sealed class ModelStateTransferValue + { + public string Key { get; set; } + public string AttemptedValue { get; set; } + public object RawValue { get; set; } + public ICollection ErrorMessages { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs index 565ebf3ce0f..0a819a2b2e0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Migrations.cs @@ -4,269 +4,268 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.Forms +namespace OrchardCore.Forms; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public async Task CreateAsync() { - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } - - public async Task CreateAsync() - { - // Form - await _contentDefinitionManager.AlterPartDefinitionAsync("FormPart", part => part - .Attachable() - .WithDescription("Turns your content item into a form.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type - .WithPart("TitlePart", part => part - .WithSettings(new TitlePartSettings { RenderTitle = false }) - .WithPosition("0") - ) - .WithPart("FormElementPart", part => - part.WithPosition("1") - ) - .WithPart("FormPart") - .WithPart("FlowPart") - .Stereotype("Widget")); - - // FormElement - await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementPart", part => part - .WithDescription("Provides attributes common to all form elements.")); - - await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementLabelPart", part => part - .Attachable() - .WithDescription("Provides a way to capture element's label.") - ); - - await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementValidationPart", part => part - .Attachable() - .WithDescription("Provides validation options to form elements.") - ); - - // FormInputElement - await _contentDefinitionManager.AlterPartDefinitionAsync("FormInputElementPart", part => part - .WithDescription("Provides attributes common to all input form elements.")); - - // Label - await _contentDefinitionManager.AlterPartDefinitionAsync("LabelPart", part => part - .WithDescription("Provides label properties.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Label", type => type - .WithPart("TitlePart", part => part - .WithSettings(new TitlePartSettings { RenderTitle = false }) - ) - .WithPart("FormElementPart") - .WithPart("LabelPart") - .Stereotype("Widget")); - - // Input - await _contentDefinitionManager.AlterPartDefinitionAsync("InputPart", part => part - .WithDescription("Provides input field properties.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Input", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("InputPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - .Stereotype("Widget")); - - // TextArea - await _contentDefinitionManager.AlterPartDefinitionAsync("TextAreaPart", part => part - .WithDescription("Provides text area properties.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("TextArea", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("TextAreaPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - .Stereotype("Widget")); - - // Select - await _contentDefinitionManager.AlterPartDefinitionAsync("SelectPart", part => part - .WithDescription("Provides select field properties.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Select", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("SelectPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - .Stereotype("Widget")); - - // Button - await _contentDefinitionManager.AlterPartDefinitionAsync("ButtonPart", part => part - .WithDescription("Provides button properties.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Button", type => type - .WithPart("FormInputElementPart") - .WithPart("FormElementPart") - .WithPart("ButtonPart") - .Stereotype("Widget")); - - // Validation Summary - await _contentDefinitionManager.AlterPartDefinitionAsync("ValidationSummaryPart", part => part - .WithDescription("Displays a validation summary.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("ValidationSummary", type => type - .WithPart("ValidationSummaryPart") - .Stereotype("Widget")); - - // Validation - await _contentDefinitionManager.AlterPartDefinitionAsync("ValidationPart", part => part - .WithDescription("Displays a field validation error.")); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Validation", type => type - .WithPart("ValidationPart") - .Stereotype("Widget")); - - // Shortcut other migration steps on new content definition schemas. - return 4; - } - - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type - .WithPart("TitlePart", part => part.MergeSettings(setting => setting.RenderTitle = false)) - ); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Label", type => type - .WithPart("TitlePart", part => part.MergeSettings(setting => setting.RenderTitle = false)) - ); - - return 2; - } - - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type - .WithPart("TitlePart", part => part - .WithPosition("0") - ) - .WithPart("FormElementPart", part => - part.WithPosition("1") - ) - ); - - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom3Async() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementLabelPart", part => part - .Attachable() - .WithDescription("Provides a way to capture element's label.") - ); - - await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementValidationPart", part => part - .Attachable() - .WithDescription("Provides validation options to form elements.") - ); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Select", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("SelectPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - ); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("Input", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("InputPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - ); - - await _contentDefinitionManager.AlterTypeDefinitionAsync("TextArea", type => type - .WithPart("FormInputElementPart", part => part - .WithPosition("1") - ) - .WithPart("FormElementPart", part => part - .WithPosition("2") - ) - .WithPart("FormElementLabelPart", part => part - .WithPosition("3") - ) - .WithPart("TextAreaPart", part => part - .WithPosition("4") - ) - .WithPart("FormElementValidationPart", part => part - .WithPosition("5") - ) - ); - - return 4; - } - - internal sealed class TitlePartSettings - { - public int Options { get; set; } - - public string Pattern { get; set; } - - [DefaultValue(true)] - public bool RenderTitle { get; set; } - } + // Form + await _contentDefinitionManager.AlterPartDefinitionAsync("FormPart", part => part + .Attachable() + .WithDescription("Turns your content item into a form.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type + .WithPart("TitlePart", part => part + .WithSettings(new TitlePartSettings { RenderTitle = false }) + .WithPosition("0") + ) + .WithPart("FormElementPart", part => + part.WithPosition("1") + ) + .WithPart("FormPart") + .WithPart("FlowPart") + .Stereotype("Widget")); + + // FormElement + await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementPart", part => part + .WithDescription("Provides attributes common to all form elements.")); + + await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementLabelPart", part => part + .Attachable() + .WithDescription("Provides a way to capture element's label.") + ); + + await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementValidationPart", part => part + .Attachable() + .WithDescription("Provides validation options to form elements.") + ); + + // FormInputElement + await _contentDefinitionManager.AlterPartDefinitionAsync("FormInputElementPart", part => part + .WithDescription("Provides attributes common to all input form elements.")); + + // Label + await _contentDefinitionManager.AlterPartDefinitionAsync("LabelPart", part => part + .WithDescription("Provides label properties.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Label", type => type + .WithPart("TitlePart", part => part + .WithSettings(new TitlePartSettings { RenderTitle = false }) + ) + .WithPart("FormElementPart") + .WithPart("LabelPart") + .Stereotype("Widget")); + + // Input + await _contentDefinitionManager.AlterPartDefinitionAsync("InputPart", part => part + .WithDescription("Provides input field properties.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Input", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("InputPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + .Stereotype("Widget")); + + // TextArea + await _contentDefinitionManager.AlterPartDefinitionAsync("TextAreaPart", part => part + .WithDescription("Provides text area properties.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("TextArea", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("TextAreaPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + .Stereotype("Widget")); + + // Select + await _contentDefinitionManager.AlterPartDefinitionAsync("SelectPart", part => part + .WithDescription("Provides select field properties.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Select", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("SelectPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + .Stereotype("Widget")); + + // Button + await _contentDefinitionManager.AlterPartDefinitionAsync("ButtonPart", part => part + .WithDescription("Provides button properties.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Button", type => type + .WithPart("FormInputElementPart") + .WithPart("FormElementPart") + .WithPart("ButtonPart") + .Stereotype("Widget")); + + // Validation Summary + await _contentDefinitionManager.AlterPartDefinitionAsync("ValidationSummaryPart", part => part + .WithDescription("Displays a validation summary.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("ValidationSummary", type => type + .WithPart("ValidationSummaryPart") + .Stereotype("Widget")); + + // Validation + await _contentDefinitionManager.AlterPartDefinitionAsync("ValidationPart", part => part + .WithDescription("Displays a field validation error.")); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Validation", type => type + .WithPart("ValidationPart") + .Stereotype("Widget")); + + // Shortcut other migration steps on new content definition schemas. + return 4; + } + + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type + .WithPart("TitlePart", part => part.MergeSettings(setting => setting.RenderTitle = false)) + ); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Label", type => type + .WithPart("TitlePart", part => part.MergeSettings(setting => setting.RenderTitle = false)) + ); + + return 2; + } + + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await _contentDefinitionManager.AlterTypeDefinitionAsync("Form", type => type + .WithPart("TitlePart", part => part + .WithPosition("0") + ) + .WithPart("FormElementPart", part => + part.WithPosition("1") + ) + ); + + return 3; + } + + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementLabelPart", part => part + .Attachable() + .WithDescription("Provides a way to capture element's label.") + ); + + await _contentDefinitionManager.AlterPartDefinitionAsync("FormElementValidationPart", part => part + .Attachable() + .WithDescription("Provides validation options to form elements.") + ); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Select", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("SelectPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + ); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("Input", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("InputPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + ); + + await _contentDefinitionManager.AlterTypeDefinitionAsync("TextArea", type => type + .WithPart("FormInputElementPart", part => part + .WithPosition("1") + ) + .WithPart("FormElementPart", part => part + .WithPosition("2") + ) + .WithPart("FormElementLabelPart", part => part + .WithPosition("3") + ) + .WithPart("TextAreaPart", part => part + .WithPosition("4") + ) + .WithPart("FormElementValidationPart", part => part + .WithPosition("5") + ) + ); + + return 4; + } + + internal sealed class TitlePartSettings + { + public int Options { get; set; } + + public string Pattern { get; set; } + + [DefaultValue(true)] + public bool RenderTitle { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ButtonPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ButtonPart.cs index 7655c7f2133..e542826e1eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ButtonPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ButtonPart.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class ButtonPart : ContentPart { - public class ButtonPart : ContentPart - { - public string Text { get; set; } - public string Type { get; set; } - } + public string Text { get; set; } + public string Type { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormElementPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormElementPart.cs index 6b4a675eb58..e0adaf194ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormElementPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormElementPart.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +/// +/// Turns a content item into a form element. +/// +public class FormElementPart : ContentPart { - /// - /// Turns a content item into a form element. - /// - public class FormElementPart : ContentPart - { - public string Id { get; set; } - } + public string Id { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormInputElementPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormInputElementPart.cs index a1467a61e9a..e9bbdb788d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormInputElementPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormInputElementPart.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +/// +/// Turns a content item into a form element that supports input. +/// +public class FormInputElementPart : ContentPart { - /// - /// Turns a content item into a form element that supports input. - /// - public class FormInputElementPart : ContentPart - { - public string Name { get; set; } - } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormPart.cs index 4f00e7d3288..ce11cec1abb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/FormPart.cs @@ -1,22 +1,21 @@ using System.ComponentModel; using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class FormPart : ContentPart { - public class FormPart : ContentPart - { - public string Action { get; set; } + public string Action { get; set; } - public string Method { get; set; } + public string Method { get; set; } - public string WorkflowTypeId { get; set; } + public string WorkflowTypeId { get; set; } - public string EncType { get; set; } + public string EncType { get; set; } - [DefaultValue(true)] - public bool EnableAntiForgeryToken { get; set; } = true; + [DefaultValue(true)] + public bool EnableAntiForgeryToken { get; set; } = true; - [DefaultValue(true)] - public bool SaveFormLocation { get; set; } = true; - } + [DefaultValue(true)] + public bool SaveFormLocation { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/InputPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/InputPart.cs index a87cd9ef436..8635f65b739 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/InputPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/InputPart.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class InputPart : ContentPart { - public class InputPart : ContentPart - { - public string Type { get; set; } - public string DefaultValue { get; set; } - public string Placeholder { get; set; } - } + public string Type { get; set; } + public string DefaultValue { get; set; } + public string Placeholder { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/LabelPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/LabelPart.cs index b56ddd7fb4d..8df9404f688 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/LabelPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/LabelPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class LabelPart : ContentPart { - public class LabelPart : ContentPart - { - public string For { get; set; } - } + public string For { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/SelectPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/SelectPart.cs index fc181045a7c..b4bbfc1857e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/SelectPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/SelectPart.cs @@ -1,25 +1,24 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class SelectPart : ContentPart { - public class SelectPart : ContentPart - { - public SelectOption[] Options { get; set; } - public string DefaultValue { get; set; } - public SelectEditorOption Editor { get; set; } - } + public SelectOption[] Options { get; set; } + public string DefaultValue { get; set; } + public SelectEditorOption Editor { get; set; } +} - public class SelectOption - { - public string Text { get; set; } +public class SelectOption +{ + public string Text { get; set; } - public string Value { get; set; } - } + public string Value { get; set; } +} - public enum SelectEditorOption - { - Dropdown, - Checkbox, - Radio - } +public enum SelectEditorOption +{ + Dropdown, + Checkbox, + Radio } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/TextAreaPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/TextAreaPart.cs index 536c8654e0f..c6cc763c029 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/TextAreaPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/TextAreaPart.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class TextAreaPart : ContentPart { - public class TextAreaPart : ContentPart - { - public string DefaultValue { get; set; } - public string Placeholder { get; set; } - } + public string DefaultValue { get; set; } + public string Placeholder { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationPart.cs index 24b9f6be5c4..f28fd0e2328 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class ValidationPart : ContentPart { - public class ValidationPart : ContentPart - { - public string For { get; set; } - } + public string For { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationSummaryPart.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationSummaryPart.cs index ef751fc2129..80a7f4cf25b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationSummaryPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Models/ValidationSummaryPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Forms.Models +namespace OrchardCore.Forms.Models; + +public class ValidationSummaryPart : ContentPart { - public class ValidationSummaryPart : ContentPart - { - public bool ModelOnly { get; set; } - } + public bool ModelOnly { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Startup.cs index 99a867ab423..4d8d419f79c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Startup.cs @@ -13,80 +13,79 @@ using OrchardCore.Modules; using OrchardCore.Workflows.Helpers; -namespace OrchardCore.Forms +namespace OrchardCore.Forms; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - - services.Configure(options => - { - options.Filters.Add(); - options.Filters.Add(); - options.Filters.Add(); - }); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); + + services.Configure(options => + { + options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + }); - services.AddScoped(); + services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddDataMigration(); - services.AddScoped(); - } + services.AddDataMigration(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Workflows")] - public sealed class WorkflowStartup : StartupBase +[RequireFeatures("OrchardCore.Workflows")] +public sealed class WorkflowStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddActivity(); - } + services.AddActivity(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ButtonPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ButtonPartEditViewModel.cs index 53dde268ab6..74b8a2c6b1f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ButtonPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ButtonPartEditViewModel.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class ButtonPartEditViewModel { - public class ButtonPartEditViewModel - { - [Required] - public string Text { get; set; } + [Required] + public string Text { get; set; } - public string Type { get; set; } - } + public string Type { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormElementPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormElementPartEditViewModel.cs index 4cab3e8eef3..9fb42340d95 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormElementPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormElementPartEditViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class FormElementPartEditViewModel { - public class FormElementPartEditViewModel - { - public string Id { get; set; } - } + public string Id { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormInputElementPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormInputElementPartEditViewModel.cs index 583daba8dd8..0b0a04bccec 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormInputElementPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormInputElementPartEditViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class FormInputElementPartEditViewModel { - public class FormInputElementPartEditViewModel - { - public string Name { get; set; } - } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormPartEditViewModel.cs index 89d1ee5f371..37b800c6994 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/FormPartEditViewModel.cs @@ -1,17 +1,16 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class FormPartEditViewModel { - public class FormPartEditViewModel - { - public string Action { get; set; } + public string Action { get; set; } - public string Method { get; set; } + public string Method { get; set; } - public string WorkflowTypeId { get; set; } + public string WorkflowTypeId { get; set; } - public string EncType { get; set; } + public string EncType { get; set; } - public bool EnableAntiForgeryToken { get; set; } = true; + public bool EnableAntiForgeryToken { get; set; } = true; - public bool SaveFormLocation { get; set; } = true; - } + public bool SaveFormLocation { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/InputPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/InputPartEditViewModel.cs index 0170e214ae4..ce4707bd6a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/InputPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/InputPartEditViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class InputPartEditViewModel { - public class InputPartEditViewModel - { - public string Type { get; set; } - public string DefaultValue { get; set; } - public string Placeholder { get; set; } - } + public string Type { get; set; } + public string DefaultValue { get; set; } + public string Placeholder { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/LabelPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/LabelPartEditViewModel.cs index e15e270bc73..09f33a446ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/LabelPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/LabelPartEditViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class LabelPartEditViewModel { - public class LabelPartEditViewModel - { - public string For { get; set; } - } + public string For { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/SelectPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/SelectPartEditViewModel.cs index 82fc04172ce..6deeaaf4d56 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/SelectPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/SelectPartEditViewModel.cs @@ -1,11 +1,10 @@ using OrchardCore.Forms.Models; -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class SelectPartEditViewModel { - public class SelectPartEditViewModel - { - public string Options { get; set; } - public string DefaultValue { get; set; } - public SelectEditorOption Editor { get; set; } - } + public string Options { get; set; } + public string DefaultValue { get; set; } + public SelectEditorOption Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/TextAreaPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/TextAreaPartEditViewModel.cs index 2406fee9846..ad1af99c73b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/TextAreaPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/TextAreaPartEditViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class TextAreaPartEditViewModel { - public class TextAreaPartEditViewModel - { - public string DefaultValue { get; set; } - public string Placeholder { get; set; } - } + public string DefaultValue { get; set; } + public string Placeholder { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ValidationPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ValidationPartEditViewModel.cs index 9af5344e2c6..029c2b28e52 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ValidationPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/ViewModels/ValidationPartEditViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Forms.ViewModels +namespace OrchardCore.Forms.ViewModels; + +public class ValidationPartEditViewModel { - public class ValidationPartEditViewModel - { - public string For { get; set; } - public string ErrorMessage { get; set; } - } + public string For { get; set; } + public string ErrorMessage { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/AddModelValidationErrorTask.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/AddModelValidationErrorTask.cs index cb2d3f7a821..edd1c209d7b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/AddModelValidationErrorTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/AddModelValidationErrorTask.cs @@ -6,50 +6,49 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.Forms.Workflows.Activities +namespace OrchardCore.Forms.Workflows.Activities; + +public class AddModelValidationErrorTask : TaskActivity { - public class AddModelValidationErrorTask : TaskActivity + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IStringLocalizer S; + + public AddModelValidationErrorTask( + IUpdateModelAccessor updateModelAccessor, + IStringLocalizer localizer + ) { - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IStringLocalizer S; - - public AddModelValidationErrorTask( - IUpdateModelAccessor updateModelAccessor, - IStringLocalizer localizer - ) - { - _updateModelAccessor = updateModelAccessor; - S = localizer; - } - - public override LocalizedString DisplayText => S["Add Model Validation Error Task"]; - - public override LocalizedString Category => S["Validation"]; - - public string Key - { - get => GetProperty(); - set => SetProperty(value); - } - - public string ErrorMessage - { - get => GetProperty(); - set => SetProperty(value); - } - - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"]); - } - - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var updater = _updateModelAccessor.ModelUpdater - ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); - - updater.ModelState.AddModelError(Key, ErrorMessage); - return Outcomes("Done"); - } + _updateModelAccessor = updateModelAccessor; + S = localizer; + } + + public override LocalizedString DisplayText => S["Add Model Validation Error Task"]; + + public override LocalizedString Category => S["Validation"]; + + public string Key + { + get => GetProperty(); + set => SetProperty(value); + } + + public string ErrorMessage + { + get => GetProperty(); + set => SetProperty(value); + } + + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"]); + } + + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var updater = _updateModelAccessor.ModelUpdater + ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); + + updater.ModelState.AddModelError(Key, ErrorMessage); + return Outcomes("Done"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/BindModelStateTask.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/BindModelStateTask.cs index e5b2a749c99..7d07ae506b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/BindModelStateTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/BindModelStateTask.cs @@ -7,47 +7,46 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.Forms.Workflows.Activities +namespace OrchardCore.Forms.Workflows.Activities; + +public class BindModelStateTask : TaskActivity { - public class BindModelStateTask : TaskActivity + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + protected readonly IStringLocalizer S; + + public BindModelStateTask( + IHttpContextAccessor httpContextAccessor, + IUpdateModelAccessor updateModelAccessor, + IStringLocalizer localizer + ) { - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - protected readonly IStringLocalizer S; - - public BindModelStateTask( - IHttpContextAccessor httpContextAccessor, - IUpdateModelAccessor updateModelAccessor, - IStringLocalizer localizer - ) - { - _updateModelAccessor = updateModelAccessor; - _httpContextAccessor = httpContextAccessor; - S = localizer; - } - - public override LocalizedString DisplayText => S["Bind Model State Task"]; + _updateModelAccessor = updateModelAccessor; + _httpContextAccessor = httpContextAccessor; + S = localizer; + } - public override LocalizedString Category => S["Validation"]; + public override LocalizedString DisplayText => S["Bind Model State Task"]; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"]); - } + public override LocalizedString Category => S["Validation"]; - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var updater = _updateModelAccessor.ModelUpdater - ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"]); + } - var httpContext = _httpContextAccessor.HttpContext; + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var updater = _updateModelAccessor.ModelUpdater + ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); - foreach (var item in httpContext.Request.Form) - { - updater.ModelState.SetModelValue(item.Key, item.Value, item.Value); - } + var httpContext = _httpContextAccessor.HttpContext; - return Outcomes("Done"); + foreach (var item in httpContext.Request.Form) + { + updater.ModelState.SetModelValue(item.Key, item.Value, item.Value); } + + return Outcomes("Done"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateAntiforgeryTokenTask.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateAntiforgeryTokenTask.cs index 37b14f587cd..4fa2cd73023 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateAntiforgeryTokenTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateAntiforgeryTokenTask.cs @@ -7,46 +7,45 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.Forms.Workflows.Activities +namespace OrchardCore.Forms.Workflows.Activities; + +public class ValidateAntiforgeryTokenTask : TaskActivity { - public class ValidateAntiforgeryTokenTask : TaskActivity + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAntiforgery _antiforgery; + protected readonly IStringLocalizer S; + + public ValidateAntiforgeryTokenTask( + IHttpContextAccessor httpContextAccessor, + IAntiforgery antiforgery, + IStringLocalizer localizer + ) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAntiforgery _antiforgery; - protected readonly IStringLocalizer S; - - public ValidateAntiforgeryTokenTask( - IHttpContextAccessor httpContextAccessor, - IAntiforgery antiforgery, - IStringLocalizer localizer - ) - { - _httpContextAccessor = httpContextAccessor; - _antiforgery = antiforgery; - S = localizer; - } + _httpContextAccessor = httpContextAccessor; + _antiforgery = antiforgery; + S = localizer; + } - public override LocalizedString DisplayText => S["Validate Antiforgery Token Task"]; + public override LocalizedString DisplayText => S["Validate Antiforgery Token Task"]; - public override LocalizedString Category => S["Validation"]; + public override LocalizedString Category => S["Validation"]; - public override bool HasEditor => false; + public override bool HasEditor => false; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Valid"], S["Invalid"]); + } + + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + if (await _antiforgery.IsRequestValidAsync(_httpContextAccessor.HttpContext)) { - return Outcomes(S["Done"], S["Valid"], S["Invalid"]); + return Outcomes("Done", "Valid"); } - - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + else { - if (await _antiforgery.IsRequestValidAsync(_httpContextAccessor.HttpContext)) - { - return Outcomes("Done", "Valid"); - } - else - { - return Outcomes("Done", "Invalid"); - } + return Outcomes("Done", "Invalid"); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormFieldTask.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormFieldTask.cs index 1defcd12ae0..114d450a415 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormFieldTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormFieldTask.cs @@ -6,62 +6,61 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.Forms.Workflows.Activities +namespace OrchardCore.Forms.Workflows.Activities; + +// TODO: Add the ability to configure various types of validators. +public class ValidateFormFieldTask : TaskActivity { - // TODO: Add the ability to configure various types of validators. - public class ValidateFormFieldTask : TaskActivity + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IStringLocalizer S; + + public ValidateFormFieldTask( + IHttpContextAccessor httpContextAccessor, + IUpdateModelAccessor updateModelAccessor, + IStringLocalizer localizer + ) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IStringLocalizer S; + _httpContextAccessor = httpContextAccessor; + _updateModelAccessor = updateModelAccessor; + S = localizer; + } - public ValidateFormFieldTask( - IHttpContextAccessor httpContextAccessor, - IUpdateModelAccessor updateModelAccessor, - IStringLocalizer localizer - ) - { - _httpContextAccessor = httpContextAccessor; - _updateModelAccessor = updateModelAccessor; - S = localizer; - } + public override LocalizedString DisplayText => S["Validate Form Field Task"]; - public override LocalizedString DisplayText => S["Validate Form Field Task"]; + public override LocalizedString Category => S["Validation"]; - public override LocalizedString Category => S["Validation"]; + public string FieldName + { + get => GetProperty(); + set => SetProperty(value); + } - public string FieldName - { - get => GetProperty(); - set => SetProperty(value); - } + public string ErrorMessage + { + get => GetProperty(); + set => SetProperty(value); + } - public string ErrorMessage - { - get => GetProperty(); - set => SetProperty(value); - } + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Valid"], S["Invalid"]); + } - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"], S["Valid"], S["Invalid"]); - } + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var form = _httpContextAccessor.HttpContext.Request.Form; + var fieldValue = form[FieldName]; + var isValid = !string.IsNullOrWhiteSpace(fieldValue); + var outcome = isValid ? "Valid" : "Invalid"; - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + if (!isValid) { - var form = _httpContextAccessor.HttpContext.Request.Form; - var fieldValue = form[FieldName]; - var isValid = !string.IsNullOrWhiteSpace(fieldValue); - var outcome = isValid ? "Valid" : "Invalid"; - - if (!isValid) - { - var updater = _updateModelAccessor.ModelUpdater; + var updater = _updateModelAccessor.ModelUpdater; - updater?.ModelState.TryAddModelError(FieldName, ErrorMessage); - } - - return Outcomes("Done", outcome); + updater?.ModelState.TryAddModelError(FieldName, ErrorMessage); } + + return Outcomes("Done", outcome); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormTask.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormTask.cs index 3477af87bfe..a8a22ff040d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Activities/ValidateFormTask.cs @@ -6,41 +6,40 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.Forms.Workflows.Activities +namespace OrchardCore.Forms.Workflows.Activities; + +public class ValidateFormTask : TaskActivity { - public class ValidateFormTask : TaskActivity + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IStringLocalizer S; + + public ValidateFormTask( + IUpdateModelAccessor updateModelAccessor, + IStringLocalizer localizer + ) + { + _updateModelAccessor = updateModelAccessor; + S = localizer; + } + + public override LocalizedString DisplayText => S["Validate Form Task"]; + + public override LocalizedString Category => S["Validation"]; + + public override bool HasEditor => false; + + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Valid"], S["Invalid"]); + } + + public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) { - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IStringLocalizer S; - - public ValidateFormTask( - IUpdateModelAccessor updateModelAccessor, - IStringLocalizer localizer - ) - { - _updateModelAccessor = updateModelAccessor; - S = localizer; - } - - public override LocalizedString DisplayText => S["Validate Form Task"]; - - public override LocalizedString Category => S["Validation"]; - - public override bool HasEditor => false; - - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Valid"], S["Invalid"]); - } - - public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var updater = _updateModelAccessor.ModelUpdater - ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); - - var isValid = updater.ModelState.ErrorCount == 0; - var outcome = isValid ? "Valid" : "Invalid"; - return Outcomes(outcome); - } + var updater = _updateModelAccessor.ModelUpdater + ?? throw new InvalidOperationException("Cannot add model validation errors when there's no Updater present."); + + var isValid = updater.ModelState.ErrorCount == 0; + var outcome = isValid ? "Valid" : "Invalid"; + return Outcomes(outcome); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/AddModelValidationErrorTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/AddModelValidationErrorTaskDisplayDriver.cs index 66c763b3d65..dcb0c61be3c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/AddModelValidationErrorTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/AddModelValidationErrorTaskDisplayDriver.cs @@ -2,20 +2,19 @@ using OrchardCore.Forms.Workflows.ViewModels; using OrchardCore.Workflows.Display; -namespace OrchardCore.Forms.Workflows.Drivers +namespace OrchardCore.Forms.Workflows.Drivers; + +public sealed class AddModelValidationErrorTaskDisplayDriver : ActivityDisplayDriver { - public sealed class AddModelValidationErrorTaskDisplayDriver : ActivityDisplayDriver + protected override void EditActivity(AddModelValidationErrorTask activity, AddModelValidationErrorTaskViewModel model) { - protected override void EditActivity(AddModelValidationErrorTask activity, AddModelValidationErrorTaskViewModel model) - { - model.Key = activity.Key; - model.ErrorMessage = activity.ErrorMessage; - } + model.Key = activity.Key; + model.ErrorMessage = activity.ErrorMessage; + } - protected override void UpdateActivity(AddModelValidationErrorTaskViewModel model, AddModelValidationErrorTask activity) - { - activity.Key = model.Key?.Trim(); - activity.ErrorMessage = model.ErrorMessage?.Trim(); - } + protected override void UpdateActivity(AddModelValidationErrorTaskViewModel model, AddModelValidationErrorTask activity) + { + activity.Key = model.Key?.Trim(); + activity.ErrorMessage = model.ErrorMessage?.Trim(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/BindModelStateTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/BindModelStateTaskDisplayDriver.cs index d59d589bca1..463056717fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/BindModelStateTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/BindModelStateTaskDisplayDriver.cs @@ -1,9 +1,8 @@ using OrchardCore.Forms.Workflows.Activities; using OrchardCore.Workflows.Display; -namespace OrchardCore.Forms.Workflows.Drivers +namespace OrchardCore.Forms.Workflows.Drivers; + +public sealed class BindModelStateTaskDisplayDriver : ActivityDisplayDriver { - public sealed class BindModelStateTaskDisplayDriver : ActivityDisplayDriver - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateAntiforgeryTokenTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateAntiforgeryTokenTaskDisplayDriver.cs index f962cf5d8fe..e4a579d4c5f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateAntiforgeryTokenTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateAntiforgeryTokenTaskDisplayDriver.cs @@ -1,9 +1,8 @@ using OrchardCore.Forms.Workflows.Activities; using OrchardCore.Workflows.Display; -namespace OrchardCore.Forms.Workflows.Drivers +namespace OrchardCore.Forms.Workflows.Drivers; + +public sealed class ValidateAntiforgeryTokenTaskDisplayDriver : ActivityDisplayDriver { - public sealed class ValidateAntiforgeryTokenTaskDisplayDriver : ActivityDisplayDriver - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormFieldTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormFieldTaskDisplayDriver.cs index 64c35446d1c..9a350678853 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormFieldTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormFieldTaskDisplayDriver.cs @@ -2,20 +2,19 @@ using OrchardCore.Forms.Workflows.ViewModels; using OrchardCore.Workflows.Display; -namespace OrchardCore.Forms.Workflows.Drivers +namespace OrchardCore.Forms.Workflows.Drivers; + +public sealed class ValidateFormFieldTaskDisplayDriver : ActivityDisplayDriver { - public sealed class ValidateFormFieldTaskDisplayDriver : ActivityDisplayDriver + protected override void EditActivity(ValidateFormFieldTask activity, ValidateFormFieldTaskViewModel model) { - protected override void EditActivity(ValidateFormFieldTask activity, ValidateFormFieldTaskViewModel model) - { - model.FieldName = activity.FieldName; - model.ErrorMessage = activity.ErrorMessage; - } + model.FieldName = activity.FieldName; + model.ErrorMessage = activity.ErrorMessage; + } - protected override void UpdateActivity(ValidateFormFieldTaskViewModel model, ValidateFormFieldTask activity) - { - activity.FieldName = model.FieldName?.Trim(); - activity.ErrorMessage = model.ErrorMessage?.Trim(); - } + protected override void UpdateActivity(ValidateFormFieldTaskViewModel model, ValidateFormFieldTask activity) + { + activity.FieldName = model.FieldName?.Trim(); + activity.ErrorMessage = model.ErrorMessage?.Trim(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormTaskDisplayDriver.cs index 9bc14f85127..a145d329bfe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Drivers/ValidateFormTaskDisplayDriver.cs @@ -1,9 +1,8 @@ using OrchardCore.Forms.Workflows.Activities; using OrchardCore.Workflows.Display; -namespace OrchardCore.Forms.Workflows.Drivers +namespace OrchardCore.Forms.Workflows.Drivers; + +public sealed class ValidateFormTaskDisplayDriver : ActivityDisplayDriver { - public sealed class ValidateFormTaskDisplayDriver : ActivityDisplayDriver - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Startup.cs index af664a8dc17..65858e0b38e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/Startup.cs @@ -4,18 +4,17 @@ using OrchardCore.Modules; using OrchardCore.Workflows.Helpers; -namespace OrchardCore.Forms.Workflows +namespace OrchardCore.Forms.Workflows; + +[RequireFeatures("OrchardCore.Workflows")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Workflows")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - services.AddActivity(); - } + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); + services.AddActivity(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/AddModelValidationErrorTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/AddModelValidationErrorTaskViewModel.cs index eb981d7cdd1..d832e1c2f8b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/AddModelValidationErrorTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/AddModelValidationErrorTaskViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Forms.Workflows.ViewModels +namespace OrchardCore.Forms.Workflows.ViewModels; + +public class AddModelValidationErrorTaskViewModel { - public class AddModelValidationErrorTaskViewModel - { - public string Key { get; set; } - public string ErrorMessage { get; set; } - } + public string Key { get; set; } + public string ErrorMessage { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/ValidateFormFieldTaskViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/ValidateFormFieldTaskViewModel.cs index b9b323dc6d2..bab85dc0ec8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/ValidateFormFieldTaskViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Forms/Workflows/ViewModels/ValidateFormFieldTaskViewModel.cs @@ -1,13 +1,12 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Forms.Workflows.ViewModels +namespace OrchardCore.Forms.Workflows.ViewModels; + +public class ValidateFormFieldTaskViewModel { - public class ValidateFormFieldTaskViewModel - { - [Required] - public string FieldName { get; set; } + [Required] + public string FieldName { get; set; } - [Required] - public string ErrorMessage { get; set; } - } + [Required] + public string ErrorMessage { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/AdminMenuGitHubLogin.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/AdminMenuGitHubLogin.cs index 79d971b455e..7d5e8d715a2 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/AdminMenuGitHubLogin.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/AdminMenuGitHubLogin.cs @@ -3,44 +3,43 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.GitHub +namespace OrchardCore.GitHub; + +public sealed class AdminMenuGitHubLogin : INavigationProvider { - public sealed class AdminMenuGitHubLogin : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", GitHubConstants.Features.GitHubAuthentication }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", GitHubConstants.Features.GitHubAuthentication }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenuGitHubLogin(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenuGitHubLogin(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Security"], security => security - .Add(S["Authentication"], authentication => authentication - .Add(S["GitHub"], S["GitHub"].PrefixPosition(), settings => settings - .AddClass("github") - .Id("github") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageGitHubAuthentication) - .LocalNav() - ) + builder + .Add(S["Security"], security => security + .Add(S["Authentication"], authentication => authentication + .Add(S["GitHub"], S["GitHub"].PrefixPosition(), settings => settings + .AddClass("github") + .Id("github") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageGitHubAuthentication) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubHandler.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubHandler.cs index 35f1fdc0115..af319d9a39f 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubHandler.cs @@ -12,98 +12,97 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace OrchardCore.GitHub.Configuration +namespace OrchardCore.GitHub.Configuration; + +public class GitHubHandler : OAuthHandler { - public class GitHubHandler : OAuthHandler + public GitHubHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } + + protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) { - public GitHubHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) + var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); + + var response = await Backchannel.SendAsync(request, Context.RequestAborted); + if (!response.IsSuccessStatusCode) { + throw new HttpRequestException($"An error occurred when retrieving GitHub user information ({response.StatusCode}). Please check if the authentication information is correct in the corresponding GitHub Application."); } - protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) - { - var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken); + var payload = (await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync())).RootElement; - var response = await Backchannel.SendAsync(request, Context.RequestAborted); - if (!response.IsSuccessStatusCode) - { - throw new HttpRequestException($"An error occurred when retrieving GitHub user information ({response.StatusCode}). Please check if the authentication information is correct in the corresponding GitHub Application."); - } - - var payload = (await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync())).RootElement; + var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); + context.RunClaimActions(); - var context = new OAuthCreatingTicketContext(new ClaimsPrincipal(identity), properties, Context, Scheme, Options, Backchannel, tokens, payload); - context.RunClaimActions(); + await Events.CreatingTicket(context); + return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); + } - await Events.CreatingTicket(context); - return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name); - } + /// + /// This code was copied from the aspnetcore repository . We should keep it in sync with it. + /// https://github.com/dotnet/aspnetcore/blob/fcd4ed7c46083cc408417763867637f232928f9b/src/Security/Authentication/OAuth/src/OAuthHandler.cs#L193 + /// This can be removed or modified when the https://github.com/dotnet/aspnetcore/issues/33351 is resolved. + /// + protected override async Task ExchangeCodeAsync(OAuthCodeExchangeContext context) + { + var tokenRequestParameters = new Dictionary() + { + { "client_id", Options.ClientId }, + { "redirect_uri", context.RedirectUri }, + { "client_secret", Options.ClientSecret }, + { "code", context.Code }, + { "grant_type", "authorization_code" }, + }; - /// - /// This code was copied from the aspnetcore repository . We should keep it in sync with it. - /// https://github.com/dotnet/aspnetcore/blob/fcd4ed7c46083cc408417763867637f232928f9b/src/Security/Authentication/OAuth/src/OAuthHandler.cs#L193 - /// This can be removed or modified when the https://github.com/dotnet/aspnetcore/issues/33351 is resolved. - /// - protected override async Task ExchangeCodeAsync(OAuthCodeExchangeContext context) + // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl + if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out var codeVerifier)) { - var tokenRequestParameters = new Dictionary() - { - { "client_id", Options.ClientId }, - { "redirect_uri", context.RedirectUri }, - { "client_secret", Options.ClientSecret }, - { "code", context.Code }, - { "grant_type", "authorization_code" }, - }; + tokenRequestParameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier!); + context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey); + } - // PKCE https://tools.ietf.org/html/rfc7636#section-4.5, see BuildChallengeUrl - if (context.Properties.Items.TryGetValue(OAuthConstants.CodeVerifierKey, out var codeVerifier)) - { - tokenRequestParameters.Add(OAuthConstants.CodeVerifierKey, codeVerifier!); - context.Properties.Items.Remove(OAuthConstants.CodeVerifierKey); - } + var requestContent = new FormUrlEncodedContent(tokenRequestParameters!); - var requestContent = new FormUrlEncodedContent(tokenRequestParameters!); + var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Content = requestContent; + requestMessage.Version = Backchannel.DefaultRequestVersion; + var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); + if (response.IsSuccessStatusCode) + { + var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); - var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - requestMessage.Content = requestContent; - requestMessage.Version = Backchannel.DefaultRequestVersion; - var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); - if (response.IsSuccessStatusCode) + // This was added to support better error messages from the GitHub OAuth provider + if (payload.RootElement.TryGetProperty("error", out var error)) { - var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); + var output = new StringBuilder(); + output.Append(error); - // This was added to support better error messages from the GitHub OAuth provider - if (payload.RootElement.TryGetProperty("error", out var error)) + if (payload.RootElement.TryGetProperty("error_description", out var description)) { - var output = new StringBuilder(); - output.Append(error); - - if (payload.RootElement.TryGetProperty("error_description", out var description)) - { - output.Append(' '); - output.Append(description); - } - return OAuthTokenResponse.Failed(new Exception(output.ToString())); + output.Append(' '); + output.Append(description); } - - return OAuthTokenResponse.Success(payload); - } - else - { - var error = "OAuth token endpoint failure: " + await Display(response); - return OAuthTokenResponse.Failed(new Exception(error)); + return OAuthTokenResponse.Failed(new Exception(output.ToString())); } + + return OAuthTokenResponse.Success(payload); } - private static async Task Display(HttpResponseMessage response) + else { - var output = new StringBuilder(); - output.Append("Status: " + response.StatusCode + ";"); - output.Append("Headers: " + response.Headers.ToString() + ";"); - output.Append("Body: " + await response.Content.ReadAsStringAsync() + ";"); - return output.ToString(); + var error = "OAuth token endpoint failure: " + await Display(response); + return OAuthTokenResponse.Failed(new Exception(error)); } } + private static async Task Display(HttpResponseMessage response) + { + var output = new StringBuilder(); + output.Append("Status: " + response.StatusCode + ";"); + output.Append("Headers: " + response.Headers.ToString() + ";"); + output.Append("Body: " + await response.Content.ReadAsStringAsync() + ";"); + return output.ToString(); + } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptions.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptions.cs index 0aa1c8b0825..d734b4f4b35 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptions.cs @@ -3,27 +3,26 @@ using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Http; -namespace OrchardCore.GitHub.Configuration +namespace OrchardCore.GitHub.Configuration; + +/// +/// Configuration options for . +/// +public class GitHubOptions : OAuthOptions { /// - /// Configuration options for . + /// Initializes a new . /// - public class GitHubOptions : OAuthOptions + public GitHubOptions() { - /// - /// Initializes a new . - /// - public GitHubOptions() - { - CallbackPath = new PathString("/signin-github"); - AuthorizationEndpoint = GitHubDefaults.AuthorizationEndpoint; - TokenEndpoint = GitHubDefaults.TokenEndpoint; - UserInformationEndpoint = GitHubDefaults.UserInformationEndpoint; + CallbackPath = new PathString("/signin-github"); + AuthorizationEndpoint = GitHubDefaults.AuthorizationEndpoint; + TokenEndpoint = GitHubDefaults.TokenEndpoint; + UserInformationEndpoint = GitHubDefaults.UserInformationEndpoint; - ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id"); - ClaimActions.MapJsonKey("name", "login"); - ClaimActions.MapJsonKey(ClaimTypes.Email, "email", ClaimValueTypes.Email); - ClaimActions.MapJsonKey("url", "url"); - } + ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id"); + ClaimActions.MapJsonKey("name", "login"); + ClaimActions.MapJsonKey(ClaimTypes.Email, "email", ClaimValueTypes.Email); + ClaimActions.MapJsonKey("url", "url"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptionsConfiguration.cs index 46b38f58352..4392c6cd3de 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Configuration/GithubOptionsConfiguration.cs @@ -6,81 +6,80 @@ using Microsoft.Extensions.Options; using OrchardCore.GitHub.Settings; -namespace OrchardCore.GitHub.Configuration +namespace OrchardCore.GitHub.Configuration; + +public class GitHubOptionsConfiguration : + IConfigureOptions, + IConfigureNamedOptions { - public class GitHubOptionsConfiguration : - IConfigureOptions, - IConfigureNamedOptions + private readonly GitHubAuthenticationSettings _gitHubAuthenticationSettings; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ILogger _logger; + + public GitHubOptionsConfiguration( + IOptions gitHubAuthenticationSettings, + IDataProtectionProvider dataProtectionProvider, + ILogger logger) { - private readonly GitHubAuthenticationSettings _gitHubAuthenticationSettings; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly ILogger _logger; + _gitHubAuthenticationSettings = gitHubAuthenticationSettings.Value; + _dataProtectionProvider = dataProtectionProvider; + _logger = logger; + } - public GitHubOptionsConfiguration( - IOptions gitHubAuthenticationSettings, - IDataProtectionProvider dataProtectionProvider, - ILogger logger) + public void Configure(AuthenticationOptions options) + { + if (_gitHubAuthenticationSettings == null) { - _gitHubAuthenticationSettings = gitHubAuthenticationSettings.Value; - _dataProtectionProvider = dataProtectionProvider; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) + if (string.IsNullOrWhiteSpace(_gitHubAuthenticationSettings.ClientID) || + string.IsNullOrWhiteSpace(_gitHubAuthenticationSettings.ClientSecret)) { - if (_gitHubAuthenticationSettings == null) - { - return; - } - - if (string.IsNullOrWhiteSpace(_gitHubAuthenticationSettings.ClientID) || - string.IsNullOrWhiteSpace(_gitHubAuthenticationSettings.ClientSecret)) - { - _logger.LogWarning("The Github login provider is enabled but not configured."); + _logger.LogWarning("The Github login provider is enabled but not configured."); - return; - } - - // Register the OpenID Connect client handler in the authentication handlers collection. - options.AddScheme(GitHubDefaults.AuthenticationScheme, builder => - { - builder.DisplayName = "GitHub"; - builder.HandlerType = typeof(GitHubHandler); - }); + return; } - public void Configure(string name, GitHubOptions options) + // Register the OpenID Connect client handler in the authentication handlers collection. + options.AddScheme(GitHubDefaults.AuthenticationScheme, builder => { - // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. - if (!string.Equals(name, GitHubDefaults.AuthenticationScheme, StringComparison.Ordinal)) - { - return; - } + builder.DisplayName = "GitHub"; + builder.HandlerType = typeof(GitHubHandler); + }); + } - if (_gitHubAuthenticationSettings == null) - { - return; - } + public void Configure(string name, GitHubOptions options) + { + // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. + if (!string.Equals(name, GitHubDefaults.AuthenticationScheme, StringComparison.Ordinal)) + { + return; + } - options.ClientId = _gitHubAuthenticationSettings.ClientID; + if (_gitHubAuthenticationSettings == null) + { + return; + } - try - { - options.ClientSecret = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication).Unprotect(_gitHubAuthenticationSettings.ClientSecret); - } - catch - { - _logger.LogError("The GitHub Consumer Secret could not be decrypted. It may have been encrypted using a different key."); - } + options.ClientId = _gitHubAuthenticationSettings.ClientID; - if (_gitHubAuthenticationSettings.CallbackPath.HasValue) - { - options.CallbackPath = _gitHubAuthenticationSettings.CallbackPath; - } + try + { + options.ClientSecret = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication).Unprotect(_gitHubAuthenticationSettings.ClientSecret); + } + catch + { + _logger.LogError("The GitHub Consumer Secret could not be decrypted. It may have been encrypted using a different key."); + } - options.SaveTokens = _gitHubAuthenticationSettings.SaveTokens; + if (_gitHubAuthenticationSettings.CallbackPath.HasValue) + { + options.CallbackPath = _gitHubAuthenticationSettings.CallbackPath; } - public void Configure(GitHubOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.SaveTokens = _gitHubAuthenticationSettings.SaveTokens; } + + public void Configure(GitHubOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Drivers/GithubAuthenticationSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Drivers/GithubAuthenticationSettingsDisplayDriver.cs index 4ae40d0b0d0..abda34fd93d 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Drivers/GithubAuthenticationSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Drivers/GithubAuthenticationSettingsDisplayDriver.cs @@ -12,95 +12,94 @@ using OrchardCore.GitHub.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.GitHub.Drivers +namespace OrchardCore.GitHub.Drivers; + +public sealed class GitHubAuthenticationSettingsDisplayDriver : SiteDisplayDriver { - public sealed class GitHubAuthenticationSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public GitHubAuthenticationSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IDataProtectionProvider dataProtectionProvider, + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _dataProtectionProvider = dataProtectionProvider; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + protected override string SettingsGroupId + => GitHubConstants.Features.GitHubAuthentication; - public GitHubAuthenticationSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IDataProtectionProvider dataProtectionProvider, - IHttpContextAccessor httpContextAccessor, - ILogger logger) + public override async Task EditAsync(ISite site, GitHubAuthenticationSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGitHubAuthentication)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _dataProtectionProvider = dataProtectionProvider; - _httpContextAccessor = httpContextAccessor; - _logger = logger; + return null; } - protected override string SettingsGroupId - => GitHubConstants.Features.GitHubAuthentication; - - public override async Task EditAsync(ISite site, GitHubAuthenticationSettings settings, BuildEditorContext context) + return Initialize("GitHubAuthenticationSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGitHubAuthentication)) + model.ClientID = settings.ClientID; + if (!string.IsNullOrWhiteSpace(settings.ClientSecret)) { - return null; - } - - return Initialize("GitHubAuthenticationSettings_Edit", model => - { - model.ClientID = settings.ClientID; - if (!string.IsNullOrWhiteSpace(settings.ClientSecret)) + try { - try - { - var protector = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication); - model.ClientSecret = protector.Unprotect(settings.ClientSecret); - } - catch (CryptographicException) - { - _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); - model.ClientSecret = string.Empty; - model.HasDecryptionError = true; - } + var protector = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication); + model.ClientSecret = protector.Unprotect(settings.ClientSecret); } - else + catch (CryptographicException) { + _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); model.ClientSecret = string.Empty; + model.HasDecryptionError = true; } - if (settings.CallbackPath.HasValue) - { - model.CallbackUrl = settings.CallbackPath.Value; - } - model.SaveTokens = settings.SaveTokens; - }).Location("Content:5") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, GitHubAuthenticationSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGitHubAuthentication)) + } + else + { + model.ClientSecret = string.Empty; + } + if (settings.CallbackPath.HasValue) { - return null; + model.CallbackUrl = settings.CallbackPath.Value; } + model.SaveTokens = settings.SaveTokens; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - var model = new GitHubAuthenticationSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ISite site, GitHubAuthenticationSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGitHubAuthentication)) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - var protector = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication); + var model = new GitHubAuthenticationSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.ClientID = model.ClientID; - settings.ClientSecret = protector.Protect(model.ClientSecret); - settings.CallbackPath = model.CallbackUrl; - settings.SaveTokens = model.SaveTokens; + if (context.Updater.ModelState.IsValid) + { + var protector = _dataProtectionProvider.CreateProtector(GitHubConstants.Features.GitHubAuthentication); - _shellReleaseManager.RequestRelease(); - } + settings.ClientID = model.ClientID; + settings.ClientSecret = protector.Protect(model.ClientSecret); + settings.CallbackPath = model.CallbackUrl; + settings.SaveTokens = model.SaveTokens; - return await EditAsync(site, settings, context); + _shellReleaseManager.RequestRelease(); } + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Extensions/OrchardCoreBuilderExtensions.cs index a83f7bcbb71..365b8d8aa0d 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,20 +2,19 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.GitHub.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureGitHubSettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureGitHubSettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_GitHub"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_GitHub"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/GithubConstants.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/GithubConstants.cs index ad9edeade0a..3664f3f4e99 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/GithubConstants.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/GithubConstants.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.GitHub +namespace OrchardCore.GitHub; + +public static class GitHubConstants { - public static class GitHubConstants + public static class Features { - public static class Features - { - public const string GitHubAuthentication = "OrchardCore.GitHub.Authentication"; - } + public const string GitHubAuthentication = "OrchardCore.GitHub.Authentication"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Recipes/GithubAuthenticationSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Recipes/GithubAuthenticationSettingsStep.cs index b04d385ee91..94b9e6327b9 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Recipes/GithubAuthenticationSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Recipes/GithubAuthenticationSettingsStep.cs @@ -6,42 +6,41 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.GitHub.Recipes +namespace OrchardCore.GitHub.Recipes; + +/// +/// This recipe step sets GitHub Account settings. +/// +public sealed class GitHubAuthenticationSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets GitHub Account settings. - /// - public sealed class GitHubAuthenticationSettingsStep : IRecipeStepHandler + private readonly IGitHubAuthenticationService _githubAuthenticationService; + + public GitHubAuthenticationSettingsStep(IGitHubAuthenticationService githubLoginService) { - private readonly IGitHubAuthenticationService _githubAuthenticationService; + _githubAuthenticationService = githubLoginService; + } - public GitHubAuthenticationSettingsStep(IGitHubAuthenticationService githubLoginService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(GitHubAuthenticationSettings), StringComparison.OrdinalIgnoreCase)) { - _githubAuthenticationService = githubLoginService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(GitHubAuthenticationSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _githubAuthenticationService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _githubAuthenticationService.LoadSettingsAsync(); - settings.ClientID = model.ConsumerKey; - settings.ClientSecret = model.ConsumerSecret; - settings.CallbackPath = model.CallbackPath; + settings.ClientID = model.ConsumerKey; + settings.ClientSecret = model.ConsumerSecret; + settings.CallbackPath = model.CallbackPath; - await _githubAuthenticationService.UpdateSettingsAsync(settings); - } + await _githubAuthenticationService.UpdateSettingsAsync(settings); } +} - public sealed class GitHubLoginSettingsStepModel - { - public string ConsumerKey { get; set; } - public string ConsumerSecret { get; set; } - public string CallbackPath { get; set; } - } +public sealed class GitHubLoginSettingsStepModel +{ + public string ConsumerKey { get; set; } + public string ConsumerSecret { get; set; } + public string CallbackPath { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Services/GithubAuthenticationService.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Services/GithubAuthenticationService.cs index 9af141f2d42..ca812aa863c 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Services/GithubAuthenticationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Services/GithubAuthenticationService.cs @@ -7,58 +7,57 @@ using OrchardCore.GitHub.Settings; using OrchardCore.Settings; -namespace OrchardCore.GitHub.Services +namespace OrchardCore.GitHub.Services; + +public class GitHubAuthenticationService : IGitHubAuthenticationService { - public class GitHubAuthenticationService : IGitHubAuthenticationService + private readonly ISiteService _siteService; + protected readonly IStringLocalizer S; + + public GitHubAuthenticationService( + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + _siteService = siteService; + S = stringLocalizer; + } - public GitHubAuthenticationService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } + public async Task UpdateSettingsAsync(GitHubAuthenticationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(GitHubAuthenticationSettings settings) + var container = await _siteService.LoadSiteSettingsAsync(); + container.Alter(aspect => { - ArgumentNullException.ThrowIfNull(settings); + aspect.ClientID = settings.ClientID; + aspect.ClientSecret = settings.ClientSecret; + aspect.CallbackPath = settings.CallbackPath; + }); - var container = await _siteService.LoadSiteSettingsAsync(); - container.Alter(aspect => - { - aspect.ClientID = settings.ClientID; - aspect.ClientSecret = settings.ClientSecret; - aspect.CallbackPath = settings.CallbackPath; - }); + await _siteService.UpdateSiteSettingsAsync(container); + } - await _siteService.UpdateSiteSettingsAsync(container); - } + public IEnumerable ValidateSettings(GitHubAuthenticationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public IEnumerable ValidateSettings(GitHubAuthenticationSettings settings) + if (string.IsNullOrWhiteSpace(settings.ClientID)) { - ArgumentNullException.ThrowIfNull(settings); - - if (string.IsNullOrWhiteSpace(settings.ClientID)) - { - yield return new ValidationResult(S["ClientID is required"], new string[] { nameof(settings.ClientID) }); - } + yield return new ValidationResult(S["ClientID is required"], new string[] { nameof(settings.ClientID) }); + } - if (string.IsNullOrWhiteSpace(settings.ClientSecret)) - { - yield return new ValidationResult(S["ClientSecret is required"], new string[] { nameof(settings.ClientSecret) }); - } + if (string.IsNullOrWhiteSpace(settings.ClientSecret)) + { + yield return new ValidationResult(S["ClientSecret is required"], new string[] { nameof(settings.ClientSecret) }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Services/IGithubAuthenticationService.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Services/IGithubAuthenticationService.cs index d106897cd14..34571fb275a 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Services/IGithubAuthenticationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Services/IGithubAuthenticationService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.GitHub.Settings; -namespace OrchardCore.GitHub.Services +namespace OrchardCore.GitHub.Services; + +public interface IGitHubAuthenticationService { - public interface IGitHubAuthenticationService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(GitHubAuthenticationSettings settings); - IEnumerable ValidateSettings(GitHubAuthenticationSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(GitHubAuthenticationSettings settings); + IEnumerable ValidateSettings(GitHubAuthenticationSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Settings/GithubAuthenticationSettings.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Settings/GithubAuthenticationSettings.cs index 373f8e9e641..c3fb558e570 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Settings/GithubAuthenticationSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Settings/GithubAuthenticationSettings.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.GitHub.Settings +namespace OrchardCore.GitHub.Settings; + +public class GitHubAuthenticationSettings { - public class GitHubAuthenticationSettings - { - public string ClientID { get; set; } - public string ClientSecret { get; set; } - public PathString CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public string ClientID { get; set; } + public string ClientSecret { get; set; } + public PathString CallbackPath { get; set; } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/Startup.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/Startup.cs index fe8eb630f47..8b4e68cc01e 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/Startup.cs @@ -14,37 +14,36 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.GitHub +namespace OrchardCore.GitHub; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } +} - [Feature(GitHubConstants.Features.GitHubAuthentication)] - public sealed class GitHubLoginStartup : StartupBase +[Feature(GitHubConstants.Features.GitHubAuthentication)] +public sealed class GitHubLoginStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddScoped, GitHubAuthenticationSettingsDisplayDriver>(); - services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddSingleton(); + services.AddScoped, GitHubAuthenticationSettingsDisplayDriver>(); + services.AddScoped(); + services.AddRecipeExecutionStep(); - services.AddTransient, GitHubAuthenticationSettingsConfiguration>(); + services.AddTransient, GitHubAuthenticationSettingsConfiguration>(); - // Register the options initializers required by the GitHub Handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers: - ServiceDescriptor.Transient, GitHubOptionsConfiguration>(), - ServiceDescriptor.Transient, GitHubOptionsConfiguration>(), - // Built-in initializers: - ServiceDescriptor.Transient, OAuthPostConfigureOptions>() - }); - } + // Register the options initializers required by the GitHub Handler. + services.TryAddEnumerable(new[] + { + // Orchard-specific initializers: + ServiceDescriptor.Transient, GitHubOptionsConfiguration>(), + ServiceDescriptor.Transient, GitHubOptionsConfiguration>(), + // Built-in initializers: + ServiceDescriptor.Transient, OAuthPostConfigureOptions>() + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.GitHub/ViewModels/GithubAuthenticationSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.GitHub/ViewModels/GithubAuthenticationSettingsViewModel.cs index 9cbed6ea17d..7d2237c694d 100644 --- a/src/OrchardCore.Modules/OrchardCore.GitHub/ViewModels/GithubAuthenticationSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.GitHub/ViewModels/GithubAuthenticationSettingsViewModel.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.GitHub.ViewModels +namespace OrchardCore.GitHub.ViewModels; + +public class GitHubAuthenticationSettingsViewModel { - public class GitHubAuthenticationSettingsViewModel - { - [Required(AllowEmptyStrings = false, ErrorMessage = "API key is required")] - public string ClientID { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "API key is required")] + public string ClientID { get; set; } - [Required(AllowEmptyStrings = false, ErrorMessage = "API secret key is required")] - public string ClientSecret { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "API secret key is required")] + public string ClientSecret { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackUrl { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackUrl { get; set; } - public bool SaveTokens { get; set; } + public bool SaveTokens { get; set; } - public bool HasDecryptionError { get; set; } - } + public bool HasDecryptionError { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Drivers/GoogleAnalyticsSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Drivers/GoogleAnalyticsSettingsDisplayDriver.cs index 142edbe26fa..3e6dec196be 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Drivers/GoogleAnalyticsSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Drivers/GoogleAnalyticsSettingsDisplayDriver.cs @@ -8,56 +8,55 @@ using OrchardCore.Google.Analytics.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Google.Analytics.Drivers +namespace OrchardCore.Google.Analytics.Drivers; + +public sealed class GoogleAnalyticsSettingsDisplayDriver : SiteDisplayDriver { - public sealed class GoogleAnalyticsSettingsDisplayDriver : SiteDisplayDriver - { - protected override string SettingsGroupId - => GoogleConstants.Features.GoogleAnalytics; + protected override string SettingsGroupId + => GoogleConstants.Features.GoogleAnalytics; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; - public GoogleAnalyticsSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) - { - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - } + public GoogleAnalyticsSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) + { + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } - public override async Task EditAsync(ISite site, GoogleAnalyticsSettings settings, BuildEditorContext context) + public override async Task EditAsync(ISite site, GoogleAnalyticsSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAnalytics)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAnalytics)) - { - return null; - } - - return Initialize("GoogleAnalyticsSettings_Edit", model => - { - model.TrackingID = settings.TrackingID; - }).Location("Content:5") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, GoogleAnalyticsSettings settings, UpdateEditorContext context) + return Initialize("GoogleAnalyticsSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAnalytics)) - { - return null; - } + model.TrackingID = settings.TrackingID; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - var model = new GoogleAnalyticsSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ISite site, GoogleAnalyticsSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAnalytics)) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - settings.TrackingID = model.TrackingID; - } + var model = new GoogleAnalyticsSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - return await EditAsync(site, settings, context); + if (context.Updater.ModelState.IsValid) + { + settings.TrackingID = model.TrackingID; } + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/GoogleAnalyticsFilter.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/GoogleAnalyticsFilter.cs index 6ef94065446..e49cfac7663 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/GoogleAnalyticsFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/GoogleAnalyticsFilter.cs @@ -7,47 +7,46 @@ using OrchardCore.ResourceManagement; using OrchardCore.Settings; -namespace OrchardCore.Google.Analytics +namespace OrchardCore.Google.Analytics; + +public sealed class GoogleAnalyticsFilter : IAsyncResultFilter { - public sealed class GoogleAnalyticsFilter : IAsyncResultFilter - { - private readonly IResourceManager _resourceManager; - private readonly ISiteService _siteService; + private readonly IResourceManager _resourceManager; + private readonly ISiteService _siteService; - private HtmlString _scriptsCache; + private HtmlString _scriptsCache; - public GoogleAnalyticsFilter( - IResourceManager resourceManager, - ISiteService siteService) - { - _resourceManager = resourceManager; - _siteService = siteService; - } + public GoogleAnalyticsFilter( + IResourceManager resourceManager, + ISiteService siteService) + { + _resourceManager = resourceManager; + _siteService = siteService; + } - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // Should only run on the front-end for a full view + if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) { - // Should only run on the front-end for a full view - if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) - { - var canTrack = context.HttpContext.Features.Get()?.CanTrack ?? true; + var canTrack = context.HttpContext.Features.Get()?.CanTrack ?? true; - if (_scriptsCache == null && canTrack) - { - var settings = await _siteService.GetSettingsAsync(); - - if (!string.IsNullOrWhiteSpace(settings?.TrackingID)) - { - _scriptsCache = new HtmlString($"\n\n\n"); - } - } + if (_scriptsCache == null && canTrack) + { + var settings = await _siteService.GetSettingsAsync(); - if (_scriptsCache != null) + if (!string.IsNullOrWhiteSpace(settings?.TrackingID)) { - _resourceManager.RegisterHeadScript(_scriptsCache); + _scriptsCache = new HtmlString($"\n\n\n"); } } - await next.Invoke(); + if (_scriptsCache != null) + { + _resourceManager.RegisterHeadScript(_scriptsCache); + } } + + await next.Invoke(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/GoogleAnalyticsService.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/GoogleAnalyticsService.cs index 70cacb3205d..365adfe5421 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/GoogleAnalyticsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/GoogleAnalyticsService.cs @@ -2,19 +2,18 @@ using OrchardCore.Google.Analytics.Settings; using OrchardCore.Settings; -namespace OrchardCore.Google.Analytics.Services -{ - public class GoogleAnalyticsService : IGoogleAnalyticsService - { - private readonly ISiteService _siteService; +namespace OrchardCore.Google.Analytics.Services; - public GoogleAnalyticsService( - ISiteService siteService) - { - _siteService = siteService; - } +public class GoogleAnalyticsService : IGoogleAnalyticsService +{ + private readonly ISiteService _siteService; - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public GoogleAnalyticsService( + ISiteService siteService) + { + _siteService = siteService; } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/IGoogleAnalyticsService.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/IGoogleAnalyticsService.cs index 8209c68af2b..e75d9daa21f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/IGoogleAnalyticsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Services/IGoogleAnalyticsService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using OrchardCore.Google.Analytics.Settings; -namespace OrchardCore.Google.Analytics.Services +namespace OrchardCore.Google.Analytics.Services; + +public interface IGoogleAnalyticsService { - public interface IGoogleAnalyticsService - { - Task GetSettingsAsync(); - } + Task GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Settings/GoogleAnalyticsSettings.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Settings/GoogleAnalyticsSettings.cs index 62bffbaefc4..20fa9da4d0f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Settings/GoogleAnalyticsSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/Settings/GoogleAnalyticsSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Google.Analytics.Settings +namespace OrchardCore.Google.Analytics.Settings; + +public class GoogleAnalyticsSettings { - public class GoogleAnalyticsSettings - { - public string TrackingID { get; set; } - } + public string TrackingID { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/ViewModels/GoogleAnalyticsSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/ViewModels/GoogleAnalyticsSettingsViewModel.cs index 3535f4a3f5f..98b9aac840f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Analytics/ViewModels/GoogleAnalyticsSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Analytics/ViewModels/GoogleAnalyticsSettingsViewModel.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Google.Analytics.ViewModels +namespace OrchardCore.Google.Analytics.ViewModels; + +public class GoogleAnalyticsSettingsViewModel { - public class GoogleAnalyticsSettingsViewModel - { - [Required(AllowEmptyStrings = false)] - public string TrackingID { get; set; } - } + [Required(AllowEmptyStrings = false)] + public string TrackingID { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Configuration/GoogleOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Configuration/GoogleOptionsConfiguration.cs index 47d81ecd7f7..6245be749f8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Configuration/GoogleOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Configuration/GoogleOptionsConfiguration.cs @@ -7,78 +7,77 @@ using Microsoft.Extensions.Options; using OrchardCore.Google.Authentication.Settings; -namespace OrchardCore.Google.Authentication.Configuration +namespace OrchardCore.Google.Authentication.Configuration; + +public class GoogleOptionsConfiguration : + IConfigureOptions, + IConfigureNamedOptions { - public class GoogleOptionsConfiguration : - IConfigureOptions, - IConfigureNamedOptions + private readonly GoogleAuthenticationSettings _googleAuthenticationSettings; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ILogger _logger; + + public GoogleOptionsConfiguration( + IOptions googleAuthenticationSettings, + IDataProtectionProvider dataProtectionProvider, + ILogger logger) { - private readonly GoogleAuthenticationSettings _googleAuthenticationSettings; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly ILogger _logger; + _googleAuthenticationSettings = googleAuthenticationSettings.Value; + _dataProtectionProvider = dataProtectionProvider; + _logger = logger; + } - public GoogleOptionsConfiguration( - IOptions googleAuthenticationSettings, - IDataProtectionProvider dataProtectionProvider, - ILogger logger) + public void Configure(AuthenticationOptions options) + { + if (_googleAuthenticationSettings == null) { - _googleAuthenticationSettings = googleAuthenticationSettings.Value; - _dataProtectionProvider = dataProtectionProvider; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) + if (string.IsNullOrWhiteSpace(_googleAuthenticationSettings.ClientID) || + string.IsNullOrWhiteSpace(_googleAuthenticationSettings.ClientSecret)) { - if (_googleAuthenticationSettings == null) - { - return; - } - - if (string.IsNullOrWhiteSpace(_googleAuthenticationSettings.ClientID) || - string.IsNullOrWhiteSpace(_googleAuthenticationSettings.ClientSecret)) - { - _logger.LogWarning("The Google login provider is enabled but not configured."); + _logger.LogWarning("The Google login provider is enabled but not configured."); - return; - } - - options.AddScheme(GoogleDefaults.AuthenticationScheme, builder => - { - builder.DisplayName = "Google"; - builder.HandlerType = typeof(GoogleHandler); - }); + return; } - public void Configure(string name, GoogleOptions options) + options.AddScheme(GoogleDefaults.AuthenticationScheme, builder => { - if (!string.Equals(name, GoogleDefaults.AuthenticationScheme, StringComparison.Ordinal)) - { - return; - } + builder.DisplayName = "Google"; + builder.HandlerType = typeof(GoogleHandler); + }); + } - if (_googleAuthenticationSettings == null) - { - return; - } + public void Configure(string name, GoogleOptions options) + { + if (!string.Equals(name, GoogleDefaults.AuthenticationScheme, StringComparison.Ordinal)) + { + return; + } - options.ClientId = _googleAuthenticationSettings.ClientID; - try - { - options.ClientSecret = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication).Unprotect(_googleAuthenticationSettings.ClientSecret); - } - catch - { - _logger.LogError("The Consumer Secret could not be decrypted. It may have been encrypted using a different key."); - } + if (_googleAuthenticationSettings == null) + { + return; + } - if (_googleAuthenticationSettings.CallbackPath.HasValue) - { - options.CallbackPath = _googleAuthenticationSettings.CallbackPath; - } + options.ClientId = _googleAuthenticationSettings.ClientID; + try + { + options.ClientSecret = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication).Unprotect(_googleAuthenticationSettings.ClientSecret); + } + catch + { + _logger.LogError("The Consumer Secret could not be decrypted. It may have been encrypted using a different key."); + } - options.SaveTokens = _googleAuthenticationSettings.SaveTokens; + if (_googleAuthenticationSettings.CallbackPath.HasValue) + { + options.CallbackPath = _googleAuthenticationSettings.CallbackPath; } - public void Configure(GoogleOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.SaveTokens = _googleAuthenticationSettings.SaveTokens; } + + public void Configure(GoogleOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Drivers/GoogleAuthenticationSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Drivers/GoogleAuthenticationSettingsDisplayDriver.cs index 10b39cd9469..1f3622f579a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Drivers/GoogleAuthenticationSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Drivers/GoogleAuthenticationSettingsDisplayDriver.cs @@ -12,96 +12,95 @@ using OrchardCore.Google.Authentication.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Google.Authentication.Drivers +namespace OrchardCore.Google.Authentication.Drivers; + +public sealed class GoogleAuthenticationSettingsDisplayDriver : SiteDisplayDriver { - public sealed class GoogleAuthenticationSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public GoogleAuthenticationSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IDataProtectionProvider dataProtectionProvider, + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _dataProtectionProvider = dataProtectionProvider; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + protected override string SettingsGroupId + => GoogleConstants.Features.GoogleAuthentication; - public GoogleAuthenticationSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IDataProtectionProvider dataProtectionProvider, - IHttpContextAccessor httpContextAccessor, - ILogger logger) + public override async Task EditAsync(ISite site, GoogleAuthenticationSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAuthentication)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _dataProtectionProvider = dataProtectionProvider; - _httpContextAccessor = httpContextAccessor; - _logger = logger; + return null; } - protected override string SettingsGroupId - => GoogleConstants.Features.GoogleAuthentication; - - public override async Task EditAsync(ISite site, GoogleAuthenticationSettings settings, BuildEditorContext context) + return Initialize("GoogleAuthenticationSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAuthentication)) - { - return null; - } - - return Initialize("GoogleAuthenticationSettings_Edit", model => + model.ClientID = settings.ClientID; + if (!string.IsNullOrWhiteSpace(settings.ClientSecret)) { - model.ClientID = settings.ClientID; - if (!string.IsNullOrWhiteSpace(settings.ClientSecret)) + try { - try - { - var protector = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication); - model.ClientSecret = protector.Unprotect(settings.ClientSecret); - } - catch (CryptographicException) - { - _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); - model.ClientSecret = string.Empty; - model.HasDecryptionError = true; - } + var protector = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication); + model.ClientSecret = protector.Unprotect(settings.ClientSecret); } - else + catch (CryptographicException) { + _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); model.ClientSecret = string.Empty; + model.HasDecryptionError = true; } - if (settings.CallbackPath.HasValue) - { - model.CallbackPath = settings.CallbackPath.Value; - } - model.SaveTokens = settings.SaveTokens; - }).Location("Content:5") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, GoogleAuthenticationSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAuthentication)) + } + else { - return null; + model.ClientSecret = string.Empty; } + if (settings.CallbackPath.HasValue) + { + model.CallbackPath = settings.CallbackPath.Value; + } + model.SaveTokens = settings.SaveTokens; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - var model = new GoogleAuthenticationSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - settings.ClientID = model.ClientID; - settings.CallbackPath = model.CallbackPath; - settings.SaveTokens = model.SaveTokens; + public override async Task UpdateAsync(ISite site, GoogleAuthenticationSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleAuthentication)) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - var protector = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication); + var model = new GoogleAuthenticationSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.ClientSecret = protector.Protect(model.ClientSecret); - } + settings.ClientID = model.ClientID; + settings.CallbackPath = model.CallbackPath; + settings.SaveTokens = model.SaveTokens; - _shellReleaseManager.RequestRelease(); + if (context.Updater.ModelState.IsValid) + { + var protector = _dataProtectionProvider.CreateProtector(GoogleConstants.Features.GoogleAuthentication); - return await EditAsync(site, settings, context); + settings.ClientSecret = protector.Protect(model.ClientSecret); } + + _shellReleaseManager.RequestRelease(); + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Services/GoogleAuthenticationService.cs b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Services/GoogleAuthenticationService.cs index d22f4cf8098..8930dabc507 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Services/GoogleAuthenticationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Services/GoogleAuthenticationService.cs @@ -7,71 +7,70 @@ using OrchardCore.Google.Authentication.Settings; using OrchardCore.Settings; -namespace OrchardCore.Google.Authentication.Services +namespace OrchardCore.Google.Authentication.Services; + +public class GoogleAuthenticationService : IGoogleAuthenticationService { - public class GoogleAuthenticationService : IGoogleAuthenticationService + private readonly ISiteService _siteService; + protected readonly IStringLocalizer S; + + public GoogleAuthenticationService( + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + _siteService = siteService; + S = stringLocalizer; + } - public GoogleAuthenticationService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public async Task GetSettingsAsync() + { + var container = await _siteService.GetSiteSettingsAsync(); + return container.As(); + } - public async Task GetSettingsAsync() - { - var container = await _siteService.GetSiteSettingsAsync(); - return container.As(); - } + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } + public async Task UpdateSettingsAsync(GoogleAuthenticationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(GoogleAuthenticationSettings settings) + var container = await _siteService.LoadSiteSettingsAsync(); + container.Alter(aspect => { - ArgumentNullException.ThrowIfNull(settings); - - var container = await _siteService.LoadSiteSettingsAsync(); - container.Alter(aspect => - { - aspect.ClientID = settings.ClientID; - aspect.ClientSecret = settings.ClientSecret; - aspect.CallbackPath = settings.CallbackPath; - }); + aspect.ClientID = settings.ClientID; + aspect.ClientSecret = settings.ClientSecret; + aspect.CallbackPath = settings.CallbackPath; + }); - await _siteService.UpdateSiteSettingsAsync(container); - } + await _siteService.UpdateSiteSettingsAsync(container); + } - public IEnumerable ValidateSettings(GoogleAuthenticationSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + public IEnumerable ValidateSettings(GoogleAuthenticationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - var results = new List(); + var results = new List(); - if (string.IsNullOrEmpty(settings.ClientID)) + if (string.IsNullOrEmpty(settings.ClientID)) + { + results.Add(new ValidationResult(S["The Client ID is required."], new[] { - results.Add(new ValidationResult(S["The Client ID is required."], new[] - { - nameof(settings.ClientID) - })); - } + nameof(settings.ClientID) + })); + } - if (string.IsNullOrEmpty(settings.ClientSecret)) + if (string.IsNullOrEmpty(settings.ClientSecret)) + { + results.Add(new ValidationResult(S["The Client Secret is required."], new[] { - results.Add(new ValidationResult(S["The Client Secret is required."], new[] - { - nameof(settings.ClientSecret) - })); - } - - return results; + nameof(settings.ClientSecret) + })); } + + return results; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Settings/GoogleAuthenticationSettings.cs b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Settings/GoogleAuthenticationSettings.cs index d761f2f6826..a0b85fe406a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Settings/GoogleAuthenticationSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/Settings/GoogleAuthenticationSettings.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Google.Authentication.Settings +namespace OrchardCore.Google.Authentication.Settings; + +public class GoogleAuthenticationSettings { - public class GoogleAuthenticationSettings - { - public string ClientID { get; set; } - public string ClientSecret { get; set; } - public PathString CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public string ClientID { get; set; } + public string ClientSecret { get; set; } + public PathString CallbackPath { get; set; } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/ViewModels/GoogleAuthenticationSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/ViewModels/GoogleAuthenticationSettingsViewModel.cs index 7a37b987d21..48a2b7810b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Authentication/ViewModels/GoogleAuthenticationSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Authentication/ViewModels/GoogleAuthenticationSettingsViewModel.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Google.Authentication.ViewModels +namespace OrchardCore.Google.Authentication.ViewModels; + +public class GoogleAuthenticationSettingsViewModel { - public class GoogleAuthenticationSettingsViewModel - { - [Required(AllowEmptyStrings = false, ErrorMessage = "ClientID key is required")] - public string ClientID { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "ClientID key is required")] + public string ClientID { get; set; } - [Required(AllowEmptyStrings = false, ErrorMessage = "ClientSecret is required")] - public string ClientSecret { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "ClientSecret is required")] + public string ClientSecret { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackPath { get; set; } - public bool SaveTokens { get; set; } + public bool SaveTokens { get; set; } - public bool HasDecryptionError { get; set; } - } + public bool HasDecryptionError { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Google/Extensions/OrchardCoreBuilderExtensions.cs index 9d36371cb00..ff26fc7550b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,20 +2,19 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Google.Authentication.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureGoogleSettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureGoogleSettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Google"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Google"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationAdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationAdminMenu.cs index 4c17667517b..3dfde661c0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationAdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationAdminMenu.cs @@ -3,121 +3,120 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Google +namespace OrchardCore.Google; + +public sealed class GoogleAuthenticationAdminMenu : INavigationProvider { - public sealed class GoogleAuthenticationAdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", GoogleConstants.Features.GoogleAuthentication }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", GoogleConstants.Features.GoogleAuthentication }, + }; + + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public GoogleAuthenticationAdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public GoogleAuthenticationAdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Security"], security => security - .Add(S["Authentication"], authentication => authentication - .Add(S["Google"], S["Google"].PrefixPosition(), google => google - .AddClass("google") - .Id("google") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageGoogleAuthentication) - .LocalNav() - ) + builder + .Add(S["Security"], security => security + .Add(S["Authentication"], authentication => authentication + .Add(S["Google"], S["Google"].PrefixPosition(), google => google + .AddClass("google") + .Id("google") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageGoogleAuthentication) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } +} - public sealed class GoogleAnalyticsAdminMenu : INavigationProvider +public sealed class GoogleAnalyticsAdminMenu : INavigationProvider +{ + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", GoogleConstants.Features.GoogleAnalytics }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", GoogleConstants.Features.GoogleAnalytics }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public GoogleAnalyticsAdminMenu(IStringLocalizer localizer) + public GoogleAnalyticsAdminMenu(IStringLocalizer localizer) + { + S = localizer; + } + + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Google Analytics"], S["Google Analytics"].PrefixPosition(), google => google - .AddClass("googleAnalytics").Id("googleAnalytics") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageGoogleAnalytics) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Google Analytics"], S["Google Analytics"].PrefixPosition(), google => google + .AddClass("googleAnalytics").Id("googleAnalytics") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageGoogleAnalytics) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } +} - public sealed class GoogleTagManagerAdminMenu : INavigationProvider +public sealed class GoogleTagManagerAdminMenu : INavigationProvider +{ + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", GoogleConstants.Features.GoogleTagManager }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", GoogleConstants.Features.GoogleTagManager }, + }; + + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public GoogleTagManagerAdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public GoogleTagManagerAdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Google Tag Manager"], S["Google Tag Manager"].PrefixPosition(), google => google - .AddClass("googleTagManager") - .Id("googleTagManager") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageGoogleTagManager) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Google Tag Manager"], S["Google Tag Manager"].PrefixPosition(), google => google + .AddClass("googleTagManager") + .Id("googleTagManager") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageGoogleTagManager) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationStartup.cs b/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationStartup.cs index 3810747b0b1..6a077e7da54 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/GoogleAuthenticationStartup.cs @@ -23,92 +23,91 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.Google +namespace OrchardCore.Google; + +[Feature(GoogleConstants.Features.GoogleAuthentication)] +public sealed class GoogleAuthenticationStartup : StartupBase { - [Feature(GoogleConstants.Features.GoogleAuthentication)] - public sealed class GoogleAuthenticationStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.AddScoped(); + services.AddSingleton(); + services.AddScoped, GoogleAuthenticationSettingsDisplayDriver>(); + services.AddScoped(); + // Register the options initializers required by the Google Handler. + services.TryAddEnumerable(new[] { - services.AddScoped(); - services.AddSingleton(); - services.AddScoped, GoogleAuthenticationSettingsDisplayDriver>(); - services.AddScoped(); - // Register the options initializers required by the Google Handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers: - ServiceDescriptor.Transient, GoogleOptionsConfiguration>(), - ServiceDescriptor.Transient, GoogleOptionsConfiguration>(), - // Built-in initializers: - ServiceDescriptor.Transient, OAuthPostConfigureOptions>() - }); + // Orchard-specific initializers: + ServiceDescriptor.Transient, GoogleOptionsConfiguration>(), + ServiceDescriptor.Transient, GoogleOptionsConfiguration>(), + // Built-in initializers: + ServiceDescriptor.Transient, OAuthPostConfigureOptions>() + }); - services.AddTransient, GoogleAuthenticationSettingsConfiguration>(); - } + services.AddTransient, GoogleAuthenticationSettingsConfiguration>(); } +} - [Feature(GoogleConstants.Features.GoogleAnalytics)] - public sealed class GoogleAnalyticsStartup : StartupBase +[Feature(GoogleConstants.Features.GoogleAnalytics)] +public sealed class GoogleAnalyticsStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddSingleton(); + services.AddScoped(); + services.AddSingleton(); - services.AddScoped, GoogleAnalyticsSettingsDisplayDriver>(); - services.AddScoped(); - services.Configure((options) => - { - options.Filters.Add(); - }); - } + services.AddScoped, GoogleAnalyticsSettingsDisplayDriver>(); + services.AddScoped(); + services.Configure((options) => + { + options.Filters.Add(); + }); } +} - [Feature(GoogleConstants.Features.GoogleTagManager)] - public sealed class GoogleTagManagerStartup : StartupBase +[Feature(GoogleConstants.Features.GoogleTagManager)] +public sealed class GoogleTagManagerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddSingleton(); + services.AddScoped(); + services.AddSingleton(); - services.AddScoped, GoogleTagManagerSettingsDisplayDriver>(); - services.AddScoped(); - services.Configure((options) => - { - options.Filters.Add(); - }); - } + services.AddScoped, GoogleTagManagerSettingsDisplayDriver>(); + services.AddScoped(); + services.Configure((options) => + { + options.Filters.Add(); + }); } +} - [Feature(GoogleConstants.Features.GoogleAuthentication)] - [RequireFeatures("OrchardCore.Deployment")] - public sealed class GoogleAuthenticationDeploymentStartup : StartupBase +[Feature(GoogleConstants.Features.GoogleAuthentication)] +[RequireFeatures("OrchardCore.Deployment")] +public sealed class GoogleAuthenticationDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Authentication Settings"], S => S["Exports the Google Authentication settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Authentication Settings"], S => S["Exports the Google Authentication settings."]); } +} - [Feature(GoogleConstants.Features.GoogleAnalytics)] - [RequireFeatures("OrchardCore.Deployment")] - public sealed class GoogleAnalyticsDeploymentStartup : StartupBase +[Feature(GoogleConstants.Features.GoogleAnalytics)] +[RequireFeatures("OrchardCore.Deployment")] +public sealed class GoogleAnalyticsDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Analytics Settings"], S => S["Exports the Google Analytics settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Analytics Settings"], S => S["Exports the Google Analytics settings."]); } +} - [Feature(GoogleConstants.Features.GoogleTagManager)] - [RequireFeatures("OrchardCore.Deployment")] - public sealed class GoogleTagManagerDeploymentStartup : StartupBase +[Feature(GoogleConstants.Features.GoogleTagManager)] +[RequireFeatures("OrchardCore.Deployment")] +public sealed class GoogleTagManagerDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Tag Manager Settings"], S => S["Exports the Google Tag Manager settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Google Tag Manager Settings"], S => S["Exports the Google Tag Manager settings."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/GoogleConstants.cs b/src/OrchardCore.Modules/OrchardCore.Google/GoogleConstants.cs index 550ff3af711..ae3a95ab351 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/GoogleConstants.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/GoogleConstants.cs @@ -1,12 +1,11 @@ -namespace OrchardCore.Google +namespace OrchardCore.Google; + +public static class GoogleConstants { - public static class GoogleConstants + public static class Features { - public static class Features - { - public const string GoogleAuthentication = "OrchardCore.Google.GoogleAuthentication"; - public const string GoogleAnalytics = "OrchardCore.Google.Analytics"; - public const string GoogleTagManager = "OrchardCore.Google.TagManager"; - } + public const string GoogleAuthentication = "OrchardCore.Google.GoogleAuthentication"; + public const string GoogleAnalytics = "OrchardCore.Google.Analytics"; + public const string GoogleTagManager = "OrchardCore.Google.TagManager"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Drivers/GoogleTagManagerSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Drivers/GoogleTagManagerSettingsDisplayDriver.cs index 8469bba36e8..4fb87fed592 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Drivers/GoogleTagManagerSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Drivers/GoogleTagManagerSettingsDisplayDriver.cs @@ -8,56 +8,55 @@ using OrchardCore.Google.TagManager.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Google.TagManager.Drivers +namespace OrchardCore.Google.TagManager.Drivers; + +public sealed class GoogleTagManagerSettingsDisplayDriver : SiteDisplayDriver { - public sealed class GoogleTagManagerSettingsDisplayDriver : SiteDisplayDriver - { - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; - public GoogleTagManagerSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) - { - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; - } + public GoogleTagManagerSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) + { + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } - protected override string SettingsGroupId - => GoogleConstants.Features.GoogleTagManager; + protected override string SettingsGroupId + => GoogleConstants.Features.GoogleTagManager; - public override async Task EditAsync(ISite site, GoogleTagManagerSettings settings, BuildEditorContext context) + public override async Task EditAsync(ISite site, GoogleTagManagerSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleTagManager)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleTagManager)) - { - return null; - } - - return Initialize("GoogleTagManagerSettings_Edit", model => - { - model.ContainerID = settings.ContainerID; - }).Location("Content:5") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, GoogleTagManagerSettings settings, UpdateEditorContext context) + return Initialize("GoogleTagManagerSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleTagManager)) - { - return null; - } + model.ContainerID = settings.ContainerID; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - var model = new GoogleTagManagerSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ISite site, GoogleTagManagerSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageGoogleTagManager)) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - settings.ContainerID = model.ContainerID; - } + var model = new GoogleTagManagerSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - return await EditAsync(site, settings, context); + if (context.Updater.ModelState.IsValid) + { + settings.ContainerID = model.ContainerID; } + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/GoogleTagManagerFilter.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/GoogleTagManagerFilter.cs index f1ff992d1f2..f7268829caa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/GoogleTagManagerFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/GoogleTagManagerFilter.cs @@ -7,47 +7,46 @@ using OrchardCore.ResourceManagement; using OrchardCore.Settings; -namespace OrchardCore.Google.TagManager +namespace OrchardCore.Google.TagManager; + +public sealed class GoogleTagManagerFilter : IAsyncResultFilter { - public sealed class GoogleTagManagerFilter : IAsyncResultFilter - { - private readonly IResourceManager _resourceManager; - private readonly ISiteService _siteService; + private readonly IResourceManager _resourceManager; + private readonly ISiteService _siteService; - private HtmlString _scriptsCache; + private HtmlString _scriptsCache; - public GoogleTagManagerFilter( - IResourceManager resourceManager, - ISiteService siteService) - { - _resourceManager = resourceManager; - _siteService = siteService; - } + public GoogleTagManagerFilter( + IResourceManager resourceManager, + ISiteService siteService) + { + _resourceManager = resourceManager; + _siteService = siteService; + } - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // Should only run on the front-end for a full view + if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) { - // Should only run on the front-end for a full view - if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) - { - var canTrack = context.HttpContext.Features.Get()?.CanTrack ?? true; + var canTrack = context.HttpContext.Features.Get()?.CanTrack ?? true; - if (_scriptsCache == null && canTrack) - { - var settings = await _siteService.GetSettingsAsync(); - - if (!string.IsNullOrWhiteSpace(settings?.ContainerID)) - { - _scriptsCache = new HtmlString("\n\n"); - } - } + if (_scriptsCache == null && canTrack) + { + var settings = await _siteService.GetSettingsAsync(); - if (_scriptsCache != null) + if (!string.IsNullOrWhiteSpace(settings?.ContainerID)) { - _resourceManager.RegisterHeadScript(_scriptsCache); + _scriptsCache = new HtmlString("\n\n"); } } - await next.Invoke(); + if (_scriptsCache != null) + { + _resourceManager.RegisterHeadScript(_scriptsCache); + } } + + await next.Invoke(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/GoogleTagManagerService.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/GoogleTagManagerService.cs index 9462a1700e4..738f90b4308 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/GoogleTagManagerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/GoogleTagManagerService.cs @@ -2,18 +2,17 @@ using OrchardCore.Google.TagManager.Settings; using OrchardCore.Settings; -namespace OrchardCore.Google.TagManager.Services -{ - public class GoogleTagManagerService : IGoogleTagManagerService - { - private readonly ISiteService _siteService; +namespace OrchardCore.Google.TagManager.Services; - public GoogleTagManagerService(ISiteService siteService) - { - _siteService = siteService; - } +public class GoogleTagManagerService : IGoogleTagManagerService +{ + private readonly ISiteService _siteService; - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public GoogleTagManagerService(ISiteService siteService) + { + _siteService = siteService; } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/IGoogleTagManagerService.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/IGoogleTagManagerService.cs index 68ee7f6b991..62a0efcaec1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/IGoogleTagManagerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Services/IGoogleTagManagerService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using OrchardCore.Google.TagManager.Settings; -namespace OrchardCore.Google.TagManager.Services +namespace OrchardCore.Google.TagManager.Services; + +public interface IGoogleTagManagerService { - public interface IGoogleTagManagerService - { - Task GetSettingsAsync(); - } + Task GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Settings/GoogleTagManagerSettings.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Settings/GoogleTagManagerSettings.cs index 52473a804fe..767a191357a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Settings/GoogleTagManagerSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/Settings/GoogleTagManagerSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Google.TagManager.Settings +namespace OrchardCore.Google.TagManager.Settings; + +public class GoogleTagManagerSettings { - public class GoogleTagManagerSettings - { - public string ContainerID { get; set; } - } + public string ContainerID { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/ViewModels/GoogleTagManagerSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/ViewModels/GoogleTagManagerSettingsViewModel.cs index 5b957fde665..7844a01ddf1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Google/TagManager/ViewModels/GoogleTagManagerSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Google/TagManager/ViewModels/GoogleTagManagerSettingsViewModel.cs @@ -1,10 +1,9 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Google.TagManager.ViewModels +namespace OrchardCore.Google.TagManager.ViewModels; + +public class GoogleTagManagerSettingsViewModel { - public class GoogleTagManagerSettingsViewModel - { - [Required(AllowEmptyStrings = false)] - public string ContainerID { get; set; } - } + [Required(AllowEmptyStrings = false)] + public string ContainerID { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.HealthChecks/Startup.cs b/src/OrchardCore.Modules/OrchardCore.HealthChecks/Startup.cs index 882c6659aca..b2cfc0e3e78 100644 --- a/src/OrchardCore.Modules/OrchardCore.HealthChecks/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.HealthChecks/Startup.cs @@ -10,49 +10,48 @@ using OrchardCore.HealthChecks.Services; using OrchardCore.Modules; -namespace OrchardCore.HealthChecks +namespace OrchardCore.HealthChecks; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) { - private readonly IShellConfiguration _shellConfiguration; + _shellConfiguration = shellConfiguration; + } - public Startup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddHealthChecks(); - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddHealthChecks(); + services.Configure(_shellConfiguration.GetSection("OrchardCore_HealthChecks")); + } - services.Configure(_shellConfiguration.GetSection("OrchardCore_HealthChecks")); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var healthChecksOptions = serviceProvider.GetService>().Value; - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + if (healthChecksOptions.ShowDetails) { - var healthChecksOptions = serviceProvider.GetService>().Value; + var healthChecksResponseWriter = serviceProvider.GetService(); - if (healthChecksOptions.ShowDetails) + app.UseHealthChecks(healthChecksOptions.Url, new HealthCheckOptions { - var healthChecksResponseWriter = serviceProvider.GetService(); - - app.UseHealthChecks(healthChecksOptions.Url, new HealthCheckOptions + AllowCachingResponses = false, + ResultStatusCodes = { - AllowCachingResponses = false, - ResultStatusCodes = - { - [HealthStatus.Healthy] = StatusCodes.Status200OK, - [HealthStatus.Degraded] = StatusCodes.Status200OK, - [HealthStatus.Unhealthy] = StatusCodes.Status200OK - }, - ResponseWriter = healthChecksResponseWriter.WriteResponseAsync - }); - } - else - { - app.UseHealthChecks(healthChecksOptions.Url); - } + [HealthStatus.Healthy] = StatusCodes.Status200OK, + [HealthStatus.Degraded] = StatusCodes.Status200OK, + [HealthStatus.Unhealthy] = StatusCodes.Status200OK + }, + ResponseWriter = healthChecksResponseWriter.WriteResponseAsync + }); + } + else + { + app.UseHealthChecks(healthChecksOptions.Url); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteTransformer.cs b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteTransformer.cs index fd633684a77..0fa592dccbe 100644 --- a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteTransformer.cs +++ b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteTransformer.cs @@ -4,29 +4,28 @@ using Microsoft.AspNetCore.Routing; using OrchardCore.Settings; -namespace OrchardCore.HomeRoute.Routing +namespace OrchardCore.HomeRoute.Routing; + +public class HomeRouteTransformer : DynamicRouteValueTransformer { - public class HomeRouteTransformer : DynamicRouteValueTransformer + private readonly ISiteService _siteService; + + public HomeRouteTransformer(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public HomeRouteTransformer(ISiteService siteService) + public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + var homeRoute = (await _siteService.GetSiteSettingsAsync()).HomeRoute; + + if (homeRoute.Count > 0) { - _siteService = siteService; + return new RouteValueDictionary(homeRoute); } - - public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + else { - var homeRoute = (await _siteService.GetSiteSettingsAsync()).HomeRoute; - - if (homeRoute.Count > 0) - { - return new RouteValueDictionary(homeRoute); - } - else - { - return null; - } + return null; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteValuesAddressScheme.cs b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteValuesAddressScheme.cs index 9926881c3b0..4d939f2c1b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteValuesAddressScheme.cs +++ b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Routing/HomeRouteValuesAddressScheme.cs @@ -6,72 +6,71 @@ using OrchardCore.Routing; using OrchardCore.Settings; -namespace OrchardCore.HomeRoute.Routing +namespace OrchardCore.HomeRoute.Routing; + +internal sealed class HomeRouteValuesAddressScheme : IShellRouteValuesAddressScheme { - internal sealed class HomeRouteValuesAddressScheme : IShellRouteValuesAddressScheme + private readonly ISiteService _siteService; + + public HomeRouteValuesAddressScheme(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public HomeRouteValuesAddressScheme(ISiteService siteService) + public IEnumerable FindEndpoints(RouteValuesAddress address) + { + if (address.AmbientValues == null || address.ExplicitValues == null) { - _siteService = siteService; + return []; } - public IEnumerable FindEndpoints(RouteValuesAddress address) - { - if (address.AmbientValues == null || address.ExplicitValues == null) - { - return []; - } + var homeRoute = _siteService.GetSiteSettingsAsync().GetAwaiter().GetResult().HomeRoute; - var homeRoute = _siteService.GetSiteSettingsAsync().GetAwaiter().GetResult().HomeRoute; + if (Match(homeRoute, address.ExplicitValues)) + { + var routeValues = new RouteValueDictionary(address.ExplicitValues); - if (Match(homeRoute, address.ExplicitValues)) + if (address.ExplicitValues.Count > homeRoute.Count) { - var routeValues = new RouteValueDictionary(address.ExplicitValues); - - if (address.ExplicitValues.Count > homeRoute.Count) + foreach (var entry in address.ExplicitValues) { - foreach (var entry in address.ExplicitValues) + if (!homeRoute.ContainsKey(entry.Key)) { - if (!homeRoute.ContainsKey(entry.Key)) - { - routeValues.Remove(entry.Key); - } + routeValues.Remove(entry.Key); } } + } - var endpoint = new RouteEndpoint - ( - c => null, - RoutePatternFactory.Parse(string.Empty, routeValues, null), - 0, - null, - null - ); + var endpoint = new RouteEndpoint + ( + c => null, + RoutePatternFactory.Parse(string.Empty, routeValues, null), + 0, + null, + null + ); - return new[] { endpoint }; - } + return new[] { endpoint }; + } - return []; + return []; + } + + private static bool Match(RouteValueDictionary routeValues, RouteValueDictionary explicitValues) + { + if (routeValues.Count == 0) + { + return false; } - private static bool Match(RouteValueDictionary routeValues, RouteValueDictionary explicitValues) + foreach (var entry in routeValues) { - if (routeValues.Count == 0) + if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) { return false; } - - foreach (var entry in routeValues) - { - if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return true; } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Startup.cs b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Startup.cs index db07c8de113..d3a75bf6e36 100644 --- a/src/OrchardCore.Modules/OrchardCore.HomeRoute/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.HomeRoute/Startup.cs @@ -6,22 +6,21 @@ using OrchardCore.Modules; using OrchardCore.Routing; -namespace OrchardCore.HomeRoute +namespace OrchardCore.HomeRoute; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - public override int Order - => OrchardCoreConstants.ConfigureOrder.HomeRoute; + public override int Order + => OrchardCoreConstants.ConfigureOrder.HomeRoute; - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddSingleton(); - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapDynamicControllerRoute("/"); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapDynamicControllerRoute("/"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Drivers/HtmlBodyPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Html/Drivers/HtmlBodyPartDisplayDriver.cs index 434b3a61442..0c5556a4e8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Drivers/HtmlBodyPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Drivers/HtmlBodyPartDisplayDriver.cs @@ -17,88 +17,87 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Html.Drivers +namespace OrchardCore.Html.Drivers; + +public sealed class HtmlBodyPartDisplayDriver : ContentPartDisplayDriver { - public sealed class HtmlBodyPartDisplayDriver : ContentPartDisplayDriver + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly HtmlEncoder _htmlEncoder; + private readonly IShortcodeService _shortcodeService; + + internal readonly IStringLocalizer S; + + public HtmlBodyPartDisplayDriver(ILiquidTemplateManager liquidTemplateManager, + IHtmlSanitizerService htmlSanitizerService, + HtmlEncoder htmlEncoder, + IShortcodeService shortcodeService, + IStringLocalizer localizer) { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly HtmlEncoder _htmlEncoder; - private readonly IShortcodeService _shortcodeService; + _liquidTemplateManager = liquidTemplateManager; + _htmlSanitizerService = htmlSanitizerService; + _htmlEncoder = htmlEncoder; + _shortcodeService = shortcodeService; + S = localizer; + } - internal readonly IStringLocalizer S; + public override IDisplayResult Display(HtmlBodyPart HtmlBodyPart, BuildPartDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), m => BuildViewModelAsync(m, HtmlBodyPart, context)) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public HtmlBodyPartDisplayDriver(ILiquidTemplateManager liquidTemplateManager, - IHtmlSanitizerService htmlSanitizerService, - HtmlEncoder htmlEncoder, - IShortcodeService shortcodeService, - IStringLocalizer localizer) + public override IDisplayResult Edit(HtmlBodyPart HtmlBodyPart, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - _liquidTemplateManager = liquidTemplateManager; - _htmlSanitizerService = htmlSanitizerService; - _htmlEncoder = htmlEncoder; - _shortcodeService = shortcodeService; - S = localizer; - } + model.Html = HtmlBodyPart.Html; + model.ContentItem = HtmlBodyPart.ContentItem; + model.HtmlBodyPart = HtmlBodyPart; + model.TypePartDefinition = context.TypePartDefinition; + }); + } - public override IDisplayResult Display(HtmlBodyPart HtmlBodyPart, BuildPartDisplayContext context) - { - return Initialize(GetDisplayShapeType(context), m => BuildViewModelAsync(m, HtmlBodyPart, context)) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + public override async Task UpdateAsync(HtmlBodyPart model, UpdatePartEditorContext context) + { + var viewModel = new HtmlBodyPartViewModel(); - public override IDisplayResult Edit(HtmlBodyPart HtmlBodyPart, BuildPartEditorContext context) + var settings = context.TypePartDefinition.GetSettings(); + + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Html); + + if (!string.IsNullOrEmpty(viewModel.Html) && !_liquidTemplateManager.Validate(viewModel.Html, out var errors)) { - return Initialize(GetEditorShapeType(context), model => - { - model.Html = HtmlBodyPart.Html; - model.ContentItem = HtmlBodyPart.ContentItem; - model.HtmlBodyPart = HtmlBodyPart; - model.TypePartDefinition = context.TypePartDefinition; - }); + var partName = context.TypePartDefinition.DisplayName(); + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Html), S["{0} doesn't contain a valid Liquid expression. Details: {1}", partName, string.Join(" ", errors)]); } - - public override async Task UpdateAsync(HtmlBodyPart model, UpdatePartEditorContext context) + else { - var viewModel = new HtmlBodyPartViewModel(); - - var settings = context.TypePartDefinition.GetSettings(); - - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Html); + model.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(viewModel.Html) : viewModel.Html; + } - if (!string.IsNullOrEmpty(viewModel.Html) && !_liquidTemplateManager.Validate(viewModel.Html, out var errors)) - { - var partName = context.TypePartDefinition.DisplayName(); - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Html), S["{0} doesn't contain a valid Liquid expression. Details: {1}", partName, string.Join(" ", errors)]); - } - else - { - model.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(viewModel.Html) : viewModel.Html; - } + return Edit(model, context); + } - return Edit(model, context); - } + private async ValueTask BuildViewModelAsync(HtmlBodyPartViewModel model, HtmlBodyPart htmlBodyPart, BuildPartDisplayContext context) + { + model.Html = htmlBodyPart.Html; + model.HtmlBodyPart = htmlBodyPart; + model.ContentItem = htmlBodyPart.ContentItem; + var settings = context.TypePartDefinition.GetSettings(); - private async ValueTask BuildViewModelAsync(HtmlBodyPartViewModel model, HtmlBodyPart htmlBodyPart, BuildPartDisplayContext context) + if (!settings.SanitizeHtml) { - model.Html = htmlBodyPart.Html; - model.HtmlBodyPart = htmlBodyPart; - model.ContentItem = htmlBodyPart.ContentItem; - var settings = context.TypePartDefinition.GetSettings(); + model.Html = await _liquidTemplateManager.RenderStringAsync(htmlBodyPart.Html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + } - if (!settings.SanitizeHtml) + model.Html = await _shortcodeService.ProcessAsync(model.Html, + new Context { - model.Html = await _liquidTemplateManager.RenderStringAsync(htmlBodyPart.Html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } - - model.Html = await _shortcodeService.ProcessAsync(model.Html, - new Context - { - ["ContentItem"] = htmlBodyPart.ContentItem, - ["TypePartDefinition"] = context.TypePartDefinition - }); - } + ["ContentItem"] = htmlBodyPart.ContentItem, + ["TypePartDefinition"] = context.TypePartDefinition + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/HtmlBodyQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/HtmlBodyQueryObjectType.cs index d5fe4dbf428..e9c15f47033 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/HtmlBodyQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/HtmlBodyQueryObjectType.cs @@ -17,51 +17,50 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Html.GraphQL +namespace OrchardCore.Html.GraphQL; + +public class HtmlBodyQueryObjectType : ObjectGraphType { - public class HtmlBodyQueryObjectType : ObjectGraphType + public HtmlBodyQueryObjectType(IStringLocalizer S) { - public HtmlBodyQueryObjectType(IStringLocalizer S) - { - Name = "HtmlBodyPart"; - Description = S["Content stored as HTML."]; + Name = "HtmlBodyPart"; + Description = S["Content stored as HTML."]; - Field("html") - .Description(S["the HTML content"]) - .ResolveLockedAsync(RenderHtml); - } + Field("html") + .Description(S["the HTML content"]) + .ResolveLockedAsync(RenderHtml); + } - private static async ValueTask RenderHtml(IResolveFieldContext ctx) - { - var shortcodeService = ctx.RequestServices.GetRequiredService(); - var contentDefinitionManager = ctx.RequestServices.GetRequiredService(); + private static async ValueTask RenderHtml(IResolveFieldContext ctx) + { + var shortcodeService = ctx.RequestServices.GetRequiredService(); + var contentDefinitionManager = ctx.RequestServices.GetRequiredService(); - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "HtmlBodyPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "HtmlBodyPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); - var html = ctx.Source.Html; + var html = ctx.Source.Html; - if (!settings.SanitizeHtml) + if (!settings.SanitizeHtml) + { + var model = new HtmlBodyPartViewModel() { - var model = new HtmlBodyPartViewModel() - { - Html = ctx.Source.Html, - HtmlBodyPart = ctx.Source, - ContentItem = ctx.Source.ContentItem - }; - var liquidTemplateManager = ctx.RequestServices.GetRequiredService(); - var htmlEncoder = ctx.RequestServices.GetService(); - - html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, new Dictionary { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } + Html = ctx.Source.Html, + HtmlBodyPart = ctx.Source, + ContentItem = ctx.Source.ContentItem + }; + var liquidTemplateManager = ctx.RequestServices.GetRequiredService(); + var htmlEncoder = ctx.RequestServices.GetService(); - return await shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = ctx.Source.ContentItem, - ["TypePartDefinition"] = contentTypePartDefinition - }); + html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, new Dictionary { ["ContentItem"] = new ObjectValue(model.ContentItem) }); } + + return await shortcodeService.ProcessAsync(html, + new Context + { + ["ContentItem"] = ctx.Source.ContentItem, + ["TypePartDefinition"] = contentTypePartDefinition + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/Startup.cs index 90975bd1226..c68de475b43 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/GraphQL/Startup.cs @@ -3,14 +3,13 @@ using OrchardCore.Html.Models; using OrchardCore.Modules; -namespace OrchardCore.Html.GraphQL +namespace OrchardCore.Html.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - } + services.AddObjectGraphType(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Handlers/HtmlBodyPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Html/Handlers/HtmlBodyPartHandler.cs index 9783099a6cb..8c4a0beee35 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Handlers/HtmlBodyPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Handlers/HtmlBodyPartHandler.cs @@ -15,65 +15,64 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Html.Handlers +namespace OrchardCore.Html.Handlers; + +public class HtmlBodyPartHandler : ContentPartHandler { - public class HtmlBodyPartHandler : ContentPartHandler - { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IShortcodeService _shortcodeService; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IShortcodeService _shortcodeService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - public HtmlBodyPartHandler(IContentDefinitionManager contentDefinitionManager, - IShortcodeService shortcodeService, - ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder) - { - _contentDefinitionManager = contentDefinitionManager; - _shortcodeService = shortcodeService; - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - } + public HtmlBodyPartHandler(IContentDefinitionManager contentDefinitionManager, + IShortcodeService shortcodeService, + ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder) + { + _contentDefinitionManager = contentDefinitionManager; + _shortcodeService = shortcodeService; + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, HtmlBodyPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, HtmlBodyPart part) + { + return context.ForAsync(async bodyAspect => { - return context.ForAsync(async bodyAspect => + try { - try - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "HtmlBodyPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "HtmlBodyPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); - var html = part.Html; + var html = part.Html; - if (!settings.SanitizeHtml) + if (!settings.SanitizeHtml) + { + var model = new HtmlBodyPartViewModel() { - var model = new HtmlBodyPartViewModel() - { - Html = part.Html, - HtmlBodyPart = part, - ContentItem = part.ContentItem, - }; + Html = part.Html, + HtmlBodyPart = part, + ContentItem = part.ContentItem, + }; - html = await _liquidTemplateManager.RenderStringAsync(html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } + html = await _liquidTemplateManager.RenderStringAsync(html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + } - html = await _shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = part.ContentItem, - ["TypePartDefinition"] = contentTypePartDefinition - }); + html = await _shortcodeService.ProcessAsync(html, + new Context + { + ["ContentItem"] = part.ContentItem, + ["TypePartDefinition"] = contentTypePartDefinition + }); - bodyAspect.Body = new HtmlString(html); - } - catch - { - bodyAspect.Body = HtmlString.Empty; - } - }); - } + bodyAspect.Body = new HtmlString(html); + } + catch + { + bodyAspect.Body = HtmlString.Empty; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Indexing/HtmlBodyPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Html/Indexing/HtmlBodyPartIndexHandler.cs index 85dfdb996bf..98164cee31d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Indexing/HtmlBodyPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Indexing/HtmlBodyPartIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.Html.Models; using OrchardCore.Indexing; -namespace OrchardCore.Html.Indexing +namespace OrchardCore.Html.Indexing; + +public class HtmlBodyPartIndexHandler : ContentPartIndexHandler { - public class HtmlBodyPartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(HtmlBodyPart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(HtmlBodyPart part, BuildPartIndexContext context) - { - var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, part.Html, options); - } + var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, part.Html, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Media/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Html/Media/Startup.cs index 159abf93c68..13a487ed114 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Media/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Media/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.Modules; -namespace OrchardCore.Html.Media +namespace OrchardCore.Html.Media; + +[RequireFeatures("OrchardCore.Media")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Media")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Html/Migrations.cs index a6d1f9255e0..9ec1207ed32 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Migrations.cs @@ -11,141 +11,140 @@ using OrchardCore.Html.Settings; using YesSql; -namespace OrchardCore.Html +namespace OrchardCore.Html; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly ISession _session; + private readonly ILogger _logger; + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations( + IContentDefinitionManager contentDefinitionManager, + ISession session, + ILogger logger) { - private readonly ISession _session; - private readonly ILogger _logger; - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations( - IContentDefinitionManager contentDefinitionManager, - ISession session, - ILogger logger) - { - _contentDefinitionManager = contentDefinitionManager; - _session = session; - _logger = logger; - } + _contentDefinitionManager = contentDefinitionManager; + _session = session; + _logger = logger; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("HtmlBodyPart", builder => builder - .Attachable() - .WithDescription("Provides an HTML Body for your content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("HtmlBodyPart", builder => builder + .Attachable() + .WithDescription("Provides an HTML Body for your content item.")); - // Shortcut other migration steps on new content definition schemas. - return 5; - } + // Shortcut other migration steps on new content definition schemas. + return 5; + } - // This code can be removed in a later version. + // This code can be removed in a later version. #pragma warning disable CA1822 // Mark members as static - public int UpdateFrom1() - { - return 2; - } + public int UpdateFrom1() + { + return 2; + } - // This code can be removed in a later version. - public int UpdateFrom2() + // This code can be removed in a later version. + public int UpdateFrom2() #pragma warning restore CA1822 // Mark members as static - { - return 3; - } + { + return 3; + } - // This code can be removed in a later version. - public async Task UpdateFrom3Async() + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + // Update content type definitions + foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) { - // Update content type definitions - foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) + if (contentType.Parts.Any(x => x.PartDefinition.Name == "BodyPart")) { - if (contentType.Parts.Any(x => x.PartDefinition.Name == "BodyPart")) - { - await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.RemovePart("BodyPart").WithPart("HtmlBodyPart")); - } + await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.RemovePart("BodyPart").WithPart("HtmlBodyPart")); } + } - await _contentDefinitionManager.DeletePartDefinitionAsync("BodyPart"); + await _contentDefinitionManager.DeletePartDefinitionAsync("BodyPart"); - // We are patching all content item versions by moving the Title to DisplayText - // This step doesn't need to be executed for a brand new site + // We are patching all content item versions by moving the Title to DisplayText + // This step doesn't need to be executed for a brand new site - var lastDocumentId = 0L; + var lastDocumentId = 0L; - for (; ; ) + for (; ; ) + { + var contentItemVersions = await _session.Query(x => x.DocumentId > lastDocumentId).Take(10).ListAsync(); + + if (!contentItemVersions.Any()) { - var contentItemVersions = await _session.Query(x => x.DocumentId > lastDocumentId).Take(10).ListAsync(); + // No more content item version to process + break; + } - if (!contentItemVersions.Any()) + foreach (var contentItemVersion in contentItemVersions) + { + if (UpdateBody((JsonObject)contentItemVersion.Content)) { - // No more content item version to process - break; + await _session.SaveAsync(contentItemVersion); + _logger.LogInformation("A content item version's BodyPart was upgraded: {ContentItemVersionId}", contentItemVersion.ContentItemVersionId); } - foreach (var contentItemVersion in contentItemVersions) - { - if (UpdateBody((JsonObject)contentItemVersion.Content)) - { - await _session.SaveAsync(contentItemVersion); - _logger.LogInformation("A content item version's BodyPart was upgraded: {ContentItemVersionId}", contentItemVersion.ContentItemVersionId); - } + lastDocumentId = contentItemVersion.Id; + } - lastDocumentId = contentItemVersion.Id; - } + await _session.SaveChangesAsync(); + } - await _session.SaveChangesAsync(); - } + static bool UpdateBody(JsonNode content) + { + var changed = false; - static bool UpdateBody(JsonNode content) + if (content.GetValueKind() == JsonValueKind.Object) { - var changed = false; + var body = content["BodyPart"]?["Body"]?.Value(); - if (content.GetValueKind() == JsonValueKind.Object) + if (!string.IsNullOrWhiteSpace(body)) { - var body = content["BodyPart"]?["Body"]?.Value(); - - if (!string.IsNullOrWhiteSpace(body)) - { - content["HtmlBodyPart"] = new JsonObject() { ["Html"] = body }; - changed = true; - } - - foreach (var node in content.AsObject()) - { - changed = UpdateBody(node.Value) || changed; - } + content["HtmlBodyPart"] = new JsonObject() { ["Html"] = body }; + changed = true; } - if (content.GetValueKind() == JsonValueKind.Array) + foreach (var node in content.AsObject()) { - foreach (var node in content.AsArray()) - { - changed = UpdateBody(node) || changed; - } + changed = UpdateBody(node.Value) || changed; } + } - return changed; + if (content.GetValueKind() == JsonValueKind.Array) + { + foreach (var node in content.AsArray()) + { + changed = UpdateBody(node) || changed; + } } - return 4; + return changed; } - // This code can be removed in a later version. - public async Task UpdateFrom4Async() + return 4; + } + + // This code can be removed in a later version. + public async Task UpdateFrom4Async() + { + // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. + foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) { - // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. - foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) + if (contentType.Parts.Any(x => x.PartDefinition.Name == "HtmlBodyPart")) { - if (contentType.Parts.Any(x => x.PartDefinition.Name == "HtmlBodyPart")) + await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.WithPart("HtmlBodyPart", part => { - await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.WithPart("HtmlBodyPart", part => - { - part.MergeSettings(x => x.SanitizeHtml = false); - })); - } + part.MergeSettings(x => x.SanitizeHtml = false); + })); } - - return 5; } + + return 5; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Models/HtmlBodyPart.cs b/src/OrchardCore.Modules/OrchardCore.Html/Models/HtmlBodyPart.cs index 7adab182707..33e9b0c3bc3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Models/HtmlBodyPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Models/HtmlBodyPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Html.Models +namespace OrchardCore.Html.Models; + +public class HtmlBodyPart : ContentPart { - public class HtmlBodyPart : ContentPart - { - public string Html { get; set; } - } + public string Html { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/HtmlBodyMetaWeblogDriver.cs b/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/HtmlBodyMetaWeblogDriver.cs index eef7f223ad9..32ce82557e3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/HtmlBodyMetaWeblogDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/HtmlBodyMetaWeblogDriver.cs @@ -4,27 +4,26 @@ using OrchardCore.XmlRpc; using OrchardCore.XmlRpc.Models; -namespace OrchardCore.Html.RemotePublishing +namespace OrchardCore.Html.RemotePublishing; + +public sealed class HtmlBodyMetaWeblogDriver : MetaWeblogDriver { - public sealed class HtmlBodyMetaWeblogDriver : MetaWeblogDriver + public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) { - public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) + var bodyPart = contentItem.As(); + if (bodyPart == null) { - var bodyPart = contentItem.As(); - if (bodyPart == null) - { - return; - } - - rpcStruct.Set("description", bodyPart.Html); + return; } - public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + rpcStruct.Set("description", bodyPart.Html); + } + + public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + { + if (contentItem.As() != null) { - if (contentItem.As() != null) - { - contentItem.Alter(x => x.Html = rpcStruct.Optional("description")); - } + contentItem.Alter(x => x.Html = rpcStruct.Optional("description")); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/RemotePublishingStartup.cs b/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/RemotePublishingStartup.cs index aee88b66118..9c2900a878b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/RemotePublishingStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/RemotePublishing/RemotePublishingStartup.cs @@ -2,14 +2,13 @@ using OrchardCore.MetaWeblog; using OrchardCore.Modules; -namespace OrchardCore.Html.RemotePublishing +namespace OrchardCore.Html.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class RemotePublishingStartup : StartupBase { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class RemotePublishingStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettings.cs index 229a5f74688..5be82a479bc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public class HtmlBodyPartMonacoEditorSettings { - public class HtmlBodyPartMonacoEditorSettings - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - public bool AllowCustomScripts { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } + public bool AllowCustomScripts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettingsDriver.cs index 5ef86a0df24..93e656c73a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartMonacoEditorSettingsDriver.cs @@ -11,55 +11,54 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public sealed class HtmlBodyPartMonacoEditorSettingsDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class HtmlBodyPartMonacoEditorSettingsDriver : ContentTypePartDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public HtmlBodyPartMonacoEditorSettingsDriver(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public HtmlBodyPartMonacoEditorSettingsDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("HtmlBodyPartMonacoSettings_Edit", model => { - return Initialize("HtmlBodyPartMonacoSettings_Edit", model => + var settings = contentTypePartDefinition.GetSettings(); + if (string.IsNullOrWhiteSpace(settings.Options)) { - var settings = contentTypePartDefinition.GetSettings(); - if (string.IsNullOrWhiteSpace(settings.Options)) - { - settings.Options = JConvert.SerializeObject(new { automaticLayout = true, language = "html" }, JOptions.Indented); - } - model.Options = settings.Options; - }).Location("Editor"); - } + settings.Options = JConvert.SerializeObject(new { automaticLayout = true, language = "html" }, JOptions.Indented); + } + model.Options = settings.Options; + }).Location("Editor"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if (contentTypePartDefinition.Editor() == "Monaco") { - if (contentTypePartDefinition.Editor() == "Monaco") - { - var model = new MonacoSettingsViewModel(); + var model = new MonacoSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (!model.Options.IsJson()) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); - } - else + if (!model.Options.IsJson()) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); + } + else + { + var jsonSettings = JObject.Parse(model.Options); + jsonSettings["language"] = "html"; + var settings = new HtmlBodyPartMonacoEditorSettings { - var jsonSettings = JObject.Parse(model.Options); - jsonSettings["language"] = "html"; - var settings = new HtmlBodyPartMonacoEditorSettings - { - Options = jsonSettings.ToString() - }; - context.Builder.WithSettings(settings); - } + Options = jsonSettings.ToString() + }; + context.Builder.WithSettings(settings); } - - return Edit(contentTypePartDefinition, context); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettings.cs index 3b0b3036971..145a0c9e430 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettings.cs @@ -1,14 +1,13 @@ using System.ComponentModel; -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public class HtmlBodyPartSettings { - public class HtmlBodyPartSettings - { - /// - /// Whether to sanitize the html input. - /// When true liquid templating is disabled. - /// - [DefaultValue(true)] - public bool SanitizeHtml { get; set; } = true; - } + /// + /// Whether to sanitize the html input. + /// When true liquid templating is disabled. + /// + [DefaultValue(true)] + public bool SanitizeHtml { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettingsDisplayDriver.cs index c842c0610b0..f47193b4d87 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartSettingsDisplayDriver.cs @@ -6,32 +6,31 @@ using OrchardCore.Html.Models; using OrchardCore.Html.ViewModels; -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public sealed class HtmlBodyPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class HtmlBodyPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("HtmlBodyPartSettings_Edit", model => { - return Initialize("HtmlBodyPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.SanitizeHtml = settings.SanitizeHtml; - }).Location("Content:20"); - } + model.SanitizeHtml = settings.SanitizeHtml; + }).Location("Content:20"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new HtmlBodyPartSettingsViewModel(); - var settings = new HtmlBodyPartSettings(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new HtmlBodyPartSettingsViewModel(); + var settings = new HtmlBodyPartSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SanitizeHtml = model.SanitizeHtml; + settings.SanitizeHtml = model.SanitizeHtml; - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettings.cs index 81d4d40a2aa..9e610193822 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettings.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public class HtmlBodyPartTrumbowygEditorSettings { - public class HtmlBodyPartTrumbowygEditorSettings - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - public bool AllowCustomScripts { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } + public bool AllowCustomScripts { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettingsDriver.cs index b785efa9cc2..2dbd1165d3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Settings/HtmlBodyPartTrumbowygEditorSettingsDriver.cs @@ -10,64 +10,63 @@ using OrchardCore.Html.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Html.Settings +namespace OrchardCore.Html.Settings; + +public sealed class HtmlBodyPartTrumbowygEditorSettingsDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class HtmlBodyPartTrumbowygEditorSettingsDriver : ContentTypePartDefinitionDisplayDriver + internal readonly IStringLocalizer S; + + public HtmlBodyPartTrumbowygEditorSettingsDriver(IStringLocalizer stringLocalizer) { - internal readonly IStringLocalizer S; + S = stringLocalizer; + } - public HtmlBodyPartTrumbowygEditorSettingsDriver(IStringLocalizer stringLocalizer) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("HtmlBodyPartTrumbowygSettings_Edit", model => { - S = stringLocalizer; - } + var settings = contentTypePartDefinition.GetSettings(); - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + model.Options = settings.Options; + model.InsertMediaWithUrl = settings.InsertMediaWithUrl; + }).Location("Editor"); + } + + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if (contentTypePartDefinition.Editor() == "Trumbowyg") { - return Initialize("HtmlBodyPartTrumbowygSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var model = new TrumbowygSettingsViewModel(); - model.Options = settings.Options; - model.InsertMediaWithUrl = settings.InsertMediaWithUrl; - }).Location("Editor"); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - if (contentTypePartDefinition.Editor() == "Trumbowyg") + try { - var model = new TrumbowygSettingsViewModel(); + var options = model.Options.Trim(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - try + if (!options.StartsWith('{') || !options.EndsWith('}')) { - var options = model.Options.Trim(); - - if (!options.StartsWith('{') || !options.EndsWith('}')) - { - throw new Exception(); - } - - var parser = new Parser(); + throw new Exception(); + } - var optionsScript = parser.ParseScript("var config = " + options); + var parser = new Parser(); - var settings = new HtmlBodyPartTrumbowygEditorSettings - { - InsertMediaWithUrl = model.InsertMediaWithUrl, - Options = options - }; + var optionsScript = parser.ParseScript("var config = " + options); - context.Builder.WithSettings(settings); - } - catch + var settings = new HtmlBodyPartTrumbowygEditorSettings { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); - } - } + InsertMediaWithUrl = model.InsertMediaWithUrl, + Options = options + }; - return Edit(contentTypePartDefinition, context); + context.Builder.WithSettings(settings); + } + catch + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Options), S["The options are written in an incorrect format."]); + } } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Html/Startup.cs index 85cccb9d76e..68aca87d76d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/Startup.cs @@ -13,24 +13,23 @@ using OrchardCore.Indexing; using OrchardCore.Modules; -namespace OrchardCore.Html +namespace OrchardCore.Html; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.Configure(o => o.MemberAccessStrategy.Register()); + services.Configure(o => o.MemberAccessStrategy.Register()); - // Body Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Body Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartSettingsViewModel.cs index b7f3c9a45a5..10b2d1282bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Html.ViewModels +namespace OrchardCore.Html.ViewModels; + +public class HtmlBodyPartSettingsViewModel { - public class HtmlBodyPartSettingsViewModel - { - public bool SanitizeHtml { get; set; } - } + public bool SanitizeHtml { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartViewModel.cs index 64004b5cc11..127af1b3750 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/HtmlBodyPartViewModel.cs @@ -3,19 +3,18 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Html.Models; -namespace OrchardCore.Html.ViewModels +namespace OrchardCore.Html.ViewModels; + +public class HtmlBodyPartViewModel { - public class HtmlBodyPartViewModel - { - public string Html { get; set; } + public string Html { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public HtmlBodyPart HtmlBodyPart { get; set; } + [BindNever] + public HtmlBodyPart HtmlBodyPart { get; set; } - [BindNever] - public ContentTypePartDefinition TypePartDefinition { get; set; } - } + [BindNever] + public ContentTypePartDefinition TypePartDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/MonacoSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/MonacoSettingsViewModel.cs index dcd99e0bb27..98dc856ce1d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/MonacoSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/MonacoSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Html.ViewModels +namespace OrchardCore.Html.ViewModels; + +public class MonacoSettingsViewModel { - public class MonacoSettingsViewModel - { - public string Options { get; set; } - } + public string Options { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/TrumbowygSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/TrumbowygSettingsViewModel.cs index e870b65fac1..0277e328ec1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/TrumbowygSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Html/ViewModels/TrumbowygSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Html.ViewModels +namespace OrchardCore.Html.ViewModels; + +public class TrumbowygSettingsViewModel { - public class TrumbowygSettingsViewModel - { - public string Options { get; set; } - public bool InsertMediaWithUrl { get; set; } - } + public string Options { get; set; } + public bool InsertMediaWithUrl { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Https/AdminMenu.cs index 2419a177872..e9ac16d2de3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/AdminMenu.cs @@ -4,42 +4,41 @@ using OrchardCore.Https.Drivers; using OrchardCore.Navigation; -namespace OrchardCore.Https +namespace OrchardCore.Https; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", HttpsSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", HttpsSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Security"], security => security - .Add(S["Settings"], settings => settings - .Add(S["HTTPS"], S["HTTPS"].PrefixPosition(), https => https - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageHttps) - .LocalNav() - ) + builder + .Add(S["Security"], security => security + .Add(S["Settings"], settings => settings + .Add(S["HTTPS"], S["HTTPS"].PrefixPosition(), https => https + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageHttps) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/Drivers/HttpsSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Https/Drivers/HttpsSettingsDisplayDriver.cs index 0cbefdfb4e1..784e1bc3707 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/Drivers/HttpsSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/Drivers/HttpsSettingsDisplayDriver.cs @@ -11,86 +11,85 @@ using OrchardCore.Https.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Https.Drivers +namespace OrchardCore.Https.Drivers; + +public sealed class HttpsSettingsDisplayDriver : SiteDisplayDriver { - public sealed class HttpsSettingsDisplayDriver : SiteDisplayDriver - { - public const string GroupId = "Https"; + public const string GroupId = "Https"; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly INotifier _notifier; + internal readonly IHtmlLocalizer H; - internal readonly IHtmlLocalizer H; + public HttpsSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + INotifier notifier, + IHtmlLocalizer htmlLocalizer) + { + _shellReleaseManager = shellReleaseManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _notifier = notifier; + H = htmlLocalizer; + } + protected override string SettingsGroupId + => GroupId; - public HttpsSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - INotifier notifier, - IHtmlLocalizer htmlLocalizer) + public override async Task EditAsync(ISite site, HttpsSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageHttps)) { - _shellReleaseManager = shellReleaseManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _notifier = notifier; - H = htmlLocalizer; + return null; } - protected override string SettingsGroupId - => GroupId; - public override async Task EditAsync(ISite site, HttpsSettings settings, BuildEditorContext context) + context.AddTenantReloadWarningWrapper(); + + return Initialize("HttpsSettings_Edit", async model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageHttps)) + var isHttpsRequest = _httpContextAccessor.HttpContext.Request.IsHttps; + + if (!isHttpsRequest) { - return null; + await _notifier.WarningAsync(H["For safety, Enabling require HTTPS over HTTP has been prevented."]); } - context.AddTenantReloadWarningWrapper(); - - return Initialize("HttpsSettings_Edit", async model => - { - var isHttpsRequest = _httpContextAccessor.HttpContext.Request.IsHttps; - - if (!isHttpsRequest) - { - await _notifier.WarningAsync(H["For safety, Enabling require HTTPS over HTTP has been prevented."]); - } - - model.EnableStrictTransportSecurity = settings.EnableStrictTransportSecurity; - model.IsHttpsRequest = isHttpsRequest; - model.RequireHttps = settings.RequireHttps; - model.RequireHttpsPermanent = settings.RequireHttpsPermanent; - model.SslPort = settings.SslPort ?? - (isHttpsRequest && !settings.RequireHttps - ? _httpContextAccessor.HttpContext.Request.Host.Port - : null); - }).Location("Content:2") - .OnGroup(GroupId); - } + model.EnableStrictTransportSecurity = settings.EnableStrictTransportSecurity; + model.IsHttpsRequest = isHttpsRequest; + model.RequireHttps = settings.RequireHttps; + model.RequireHttpsPermanent = settings.RequireHttpsPermanent; + model.SslPort = settings.SslPort ?? + (isHttpsRequest && !settings.RequireHttps + ? _httpContextAccessor.HttpContext.Request.Host.Port + : null); + }).Location("Content:2") + .OnGroup(GroupId); + } - public override async Task UpdateAsync(ISite site, HttpsSettings settings, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, HttpsSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageHttps)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageHttps)) - { - return null; - } + return null; + } - var model = new HttpsSettingsViewModel(); + var model = new HttpsSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.EnableStrictTransportSecurity = model.EnableStrictTransportSecurity; - settings.RequireHttps = model.RequireHttps; - settings.RequireHttpsPermanent = model.RequireHttpsPermanent; - settings.SslPort = model.SslPort; + settings.EnableStrictTransportSecurity = model.EnableStrictTransportSecurity; + settings.RequireHttps = model.RequireHttps; + settings.RequireHttpsPermanent = model.RequireHttpsPermanent; + settings.SslPort = model.SslPort; - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/Services/HttpsService.cs b/src/OrchardCore.Modules/OrchardCore.Https/Services/HttpsService.cs index cc12eec7fa2..370edc5eabb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/Services/HttpsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/Services/HttpsService.cs @@ -2,18 +2,17 @@ using OrchardCore.Https.Settings; using OrchardCore.Settings; -namespace OrchardCore.Https.Services -{ - public class HttpsService : IHttpsService - { - private readonly ISiteService _siteService; +namespace OrchardCore.Https.Services; - public HttpsService(ISiteService siteService) - { - _siteService = siteService; - } +public class HttpsService : IHttpsService +{ + private readonly ISiteService _siteService; - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public HttpsService(ISiteService siteService) + { + _siteService = siteService; } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/Services/IHttpsService.cs b/src/OrchardCore.Modules/OrchardCore.Https/Services/IHttpsService.cs index eecc62f311d..8b675597812 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/Services/IHttpsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/Services/IHttpsService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using OrchardCore.Https.Settings; -namespace OrchardCore.Https.Services +namespace OrchardCore.Https.Services; + +public interface IHttpsService { - public interface IHttpsService - { - Task GetSettingsAsync(); - } + Task GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/Settings/HttpsSettings.cs b/src/OrchardCore.Modules/OrchardCore.Https/Settings/HttpsSettings.cs index 6563938c725..5440291fe4b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/Settings/HttpsSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/Settings/HttpsSettings.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Https.Settings +namespace OrchardCore.Https.Settings; + +public class HttpsSettings { - public class HttpsSettings - { - public bool EnableStrictTransportSecurity { get; set; } - public bool RequireHttps { get; set; } - public bool RequireHttpsPermanent { get; set; } - public int? SslPort { get; set; } - } + public bool EnableStrictTransportSecurity { get; set; } + public bool RequireHttps { get; set; } + public bool RequireHttpsPermanent { get; set; } + public int? SslPort { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Https/Startup.cs index 45e926e42d7..ddd66a87173 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/Startup.cs @@ -15,60 +15,59 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.Https +namespace OrchardCore.Https; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + var httpsService = serviceProvider.GetRequiredService(); + var settings = await httpsService.GetSettingsAsync(); + if (settings.RequireHttps) { - var httpsService = serviceProvider.GetRequiredService(); - var settings = await httpsService.GetSettingsAsync(); - if (settings.RequireHttps) - { - app.UseHttpsRedirection(); - } - - if (settings.EnableStrictTransportSecurity) - { - app.UseHsts(); - } + app.UseHttpsRedirection(); } - public override void ConfigureServices(IServiceCollection services) + if (settings.EnableStrictTransportSecurity) { - services.AddScoped(); - services.AddScoped, HttpsSettingsDisplayDriver>(); - services.AddSingleton(); - - services.AddScoped(); + app.UseHsts(); + } + } - services.AddOptions() - .Configure((options, service) => - { - var settings = service.GetSettingsAsync().GetAwaiter().GetResult(); - if (settings.RequireHttpsPermanent) - { - options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect; - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped, HttpsSettingsDisplayDriver>(); + services.AddSingleton(); - options.HttpsPort = settings.SslPort; - }); + services.AddScoped(); - services.AddHsts(options => + services.AddOptions() + .Configure((options, service) => { - options.Preload = false; - options.IncludeSubDomains = true; - options.MaxAge = TimeSpan.FromDays(365); + var settings = service.GetSettingsAsync().GetAwaiter().GetResult(); + if (settings.RequireHttpsPermanent) + { + options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect; + } + + options.HttpsPort = settings.SslPort; }); - } + + services.AddHsts(options => + { + options.Preload = false; + options.IncludeSubDomains = true; + options.MaxAge = TimeSpan.FromDays(365); + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Https settings"], S => S["Exports the Https settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Https settings"], S => S["Exports the Https settings."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Https/ViewModels/HttpsSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Https/ViewModels/HttpsSettingsViewModel.cs index 09d86304a42..56969293838 100644 --- a/src/OrchardCore.Modules/OrchardCore.Https/ViewModels/HttpsSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Https/ViewModels/HttpsSettingsViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Https.ViewModels +namespace OrchardCore.Https.ViewModels; + +public class HttpsSettingsViewModel { - public class HttpsSettingsViewModel - { - public bool IsHttpsRequest { get; set; } - public bool EnableStrictTransportSecurity { get; set; } - public bool RequireHttps { get; set; } - public bool RequireHttpsPermanent { get; set; } - public int? SslPort { get; set; } - } + public bool IsHttpsRequest { get; set; } + public bool EnableStrictTransportSecurity { get; set; } + public bool RequireHttps { get; set; } + public bool RequireHttpsPermanent { get; set; } + public int? SslPort { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Indexing/CreateIndexingTaskContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Indexing/CreateIndexingTaskContentHandler.cs index 30fc2388995..5321f392b2b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Indexing/CreateIndexingTaskContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Indexing/CreateIndexingTaskContentHandler.cs @@ -1,40 +1,39 @@ using System.Threading.Tasks; using OrchardCore.ContentManagement.Handlers; -namespace OrchardCore.Indexing +namespace OrchardCore.Indexing; + +public class CreateIndexingTaskContentHandler : ContentHandlerBase { - public class CreateIndexingTaskContentHandler : ContentHandlerBase + private readonly IIndexingTaskManager _indexingTaskManager; + + public CreateIndexingTaskContentHandler(IIndexingTaskManager indexingTaskManager) { - private readonly IIndexingTaskManager _indexingTaskManager; + _indexingTaskManager = indexingTaskManager; + } - public CreateIndexingTaskContentHandler(IIndexingTaskManager indexingTaskManager) - { - _indexingTaskManager = indexingTaskManager; - } + public override Task UpdatedAsync(UpdateContentContext context) + { + return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); + } - public override Task UpdatedAsync(UpdateContentContext context) - { - return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); - } + public override Task CreatedAsync(CreateContentContext context) + { + return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); + } - public override Task CreatedAsync(CreateContentContext context) - { - return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); - } + public override Task PublishedAsync(PublishContentContext context) + { + return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); + } - public override Task PublishedAsync(PublishContentContext context) + public override Task RemovedAsync(RemoveContentContext context) + { + if (context.NoActiveVersionLeft) { - return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Update); + return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Delete); } - public override Task RemovedAsync(RemoveContentContext context) - { - if (context.NoActiveVersionLeft) - { - return _indexingTaskManager.CreateTaskAsync(context.ContentItem, IndexingTaskTypes.Delete); - } - - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Indexing/IndexingTaskManager.cs b/src/OrchardCore.Modules/OrchardCore.Indexing/IndexingTaskManager.cs index 1acb5cba94e..1be70c31716 100644 --- a/src/OrchardCore.Modules/OrchardCore.Indexing/IndexingTaskManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Indexing/IndexingTaskManager.cs @@ -14,178 +14,177 @@ using OrchardCore.Modules; using YesSql; -namespace OrchardCore.Indexing.Services +namespace OrchardCore.Indexing.Services; + +/// +/// This is a scoped service that enlists tasks to be stored in the database. +/// It enlists a final task using the current such +/// that multiple calls to can be done without incurring +/// a SQL query on every single one. +/// +public class IndexingTaskManager : IIndexingTaskManager { - /// - /// This is a scoped service that enlists tasks to be stored in the database. - /// It enlists a final task using the current such - /// that multiple calls to can be done without incurring - /// a SQL query on every single one. - /// - public class IndexingTaskManager : IIndexingTaskManager + private readonly IClock _clock; + private readonly IStore _store; + private readonly IDbConnectionAccessor _dbConnectionAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + private readonly List _tasksQueue = []; + + public IndexingTaskManager( + IClock clock, + IStore store, + IDbConnectionAccessor dbConnectionAccessor, + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IClock _clock; - private readonly IStore _store; - private readonly IDbConnectionAccessor _dbConnectionAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; - - private readonly List _tasksQueue = []; - - public IndexingTaskManager( - IClock clock, - IStore store, - IDbConnectionAccessor dbConnectionAccessor, - IHttpContextAccessor httpContextAccessor, - ILogger logger) + _clock = clock; + _store = store; + _dbConnectionAccessor = dbConnectionAccessor; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + public Task CreateTaskAsync(ContentItem contentItem, IndexingTaskTypes type) + { + ArgumentNullException.ThrowIfNull(contentItem); + + // Do not index a preview content item. + if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) { - _clock = clock; - _store = store; - _dbConnectionAccessor = dbConnectionAccessor; - _httpContextAccessor = httpContextAccessor; - _logger = logger; + return Task.CompletedTask; } - public Task CreateTaskAsync(ContentItem contentItem, IndexingTaskTypes type) + if (contentItem.Id == 0) { - ArgumentNullException.ThrowIfNull(contentItem); - - // Do not index a preview content item. - if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) - { - return Task.CompletedTask; - } + // Ignore that case, when Update is called on a content item which has not be "created" yet. + return Task.CompletedTask; + } - if (contentItem.Id == 0) - { - // Ignore that case, when Update is called on a content item which has not be "created" yet. - return Task.CompletedTask; - } + var indexingTask = new IndexingTask + { + CreatedUtc = _clock.UtcNow, + ContentItemId = contentItem.ContentItemId, + Type = type + }; - var indexingTask = new IndexingTask - { - CreatedUtc = _clock.UtcNow, - ContentItemId = contentItem.ContentItemId, - Type = type - }; + if (_tasksQueue.Count == 0) + { + var tasksQueue = _tasksQueue; - if (_tasksQueue.Count == 0) - { - var tasksQueue = _tasksQueue; + // Using a local var prevents the lambda from holding a ref on this scoped service. + ShellScope.AddDeferredTask(scope => FlushAsync(scope, tasksQueue)); + } - // Using a local var prevents the lambda from holding a ref on this scoped service. - ShellScope.AddDeferredTask(scope => FlushAsync(scope, tasksQueue)); - } + _tasksQueue.Add(indexingTask); - _tasksQueue.Add(indexingTask); + return Task.CompletedTask; + } - return Task.CompletedTask; - } + private async Task FlushAsync(ShellScope scope, IEnumerable tasks) + { + var localQueue = new List(tasks); - private async Task FlushAsync(ShellScope scope, IEnumerable tasks) - { - var localQueue = new List(tasks); + var serviceProvider = scope.ServiceProvider; - var serviceProvider = scope.ServiceProvider; + var session = serviceProvider.GetService(); + var dbConnectionAccessor = serviceProvider.GetService(); + var shellSettings = serviceProvider.GetService(); + var logger = serviceProvider.GetService>(); - var session = serviceProvider.GetService(); - var dbConnectionAccessor = serviceProvider.GetService(); - var shellSettings = serviceProvider.GetService(); - var logger = serviceProvider.GetService>(); + var contentItemIds = new HashSet(); - var contentItemIds = new HashSet(); + // Remove duplicate tasks, only keep the last one. + for (var i = localQueue.Count; i > 0; i--) + { + var task = localQueue[i - 1]; - // Remove duplicate tasks, only keep the last one. - for (var i = localQueue.Count; i > 0; i--) + if (contentItemIds.Contains(task.ContentItemId)) { - var task = localQueue[i - 1]; - - if (contentItemIds.Contains(task.ContentItemId)) - { - localQueue.RemoveAt(i - 1); - } - else - { - contentItemIds.Add(task.ContentItemId); - } + localQueue.RemoveAt(i - 1); } - - // At this point, content items ids should be unique in localQueue. - var ids = localQueue.Select(x => x.ContentItemId).ToArray(); - var table = $"{session.Store.Configuration.TablePrefix}{nameof(IndexingTask)}"; - - if (logger.IsEnabled(LogLevel.Debug)) + else { - logger.LogDebug("Updating indexing tasks: {ContentItemIds}", string.Join(", ", tasks.Select(x => x.ContentItemId))); + contentItemIds.Add(task.ContentItemId); } + } - await using var connection = dbConnectionAccessor.CreateConnection(); - await connection.OpenAsync(); - - using var transaction = await connection.BeginTransactionAsync(session.Store.Configuration.IsolationLevel); - var dialect = session.Store.Configuration.SqlDialect; + // At this point, content items ids should be unique in localQueue. + var ids = localQueue.Select(x => x.ContentItemId).ToArray(); + var table = $"{session.Store.Configuration.TablePrefix}{nameof(IndexingTask)}"; - try - { - // Page delete statements to prevent the limits from IN sql statements. - var pageSize = 100; + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug("Updating indexing tasks: {ContentItemIds}", string.Join(", ", tasks.Select(x => x.ContentItemId))); + } - var deleteCmd = $"delete from {dialect.QuoteForTableName(table, _store.Configuration.Schema)} where {dialect.QuoteForColumnName("ContentItemId")} {dialect.InOperator("@Ids")};"; + await using var connection = dbConnectionAccessor.CreateConnection(); + await connection.OpenAsync(); - do - { - var pageOfIds = ids.Take(pageSize).ToArray(); + using var transaction = await connection.BeginTransactionAsync(session.Store.Configuration.IsolationLevel); + var dialect = session.Store.Configuration.SqlDialect; - if (pageOfIds.Length > 0) - { - await transaction.Connection.ExecuteAsync(deleteCmd, new { Ids = pageOfIds }, transaction); - ids = ids.Skip(pageSize).ToArray(); - } - } while (ids.Length > 0); + try + { + // Page delete statements to prevent the limits from IN sql statements. + var pageSize = 100; - var insertCmd = $"insert into {dialect.QuoteForTableName(table, _store.Configuration.Schema)} ({dialect.QuoteForColumnName("CreatedUtc")}, {dialect.QuoteForColumnName("ContentItemId")}, {dialect.QuoteForColumnName("Type")}) values (@CreatedUtc, @ContentItemId, @Type);"; - await transaction.Connection.ExecuteAsync(insertCmd, localQueue, transaction); + var deleteCmd = $"delete from {dialect.QuoteForTableName(table, _store.Configuration.Schema)} where {dialect.QuoteForColumnName("ContentItemId")} {dialect.InOperator("@Ids")};"; - await transaction.CommitAsync(); - } - catch (Exception e) + do { - await transaction.RollbackAsync(); - logger.LogError(e, "An error occurred while updating indexing tasks"); + var pageOfIds = ids.Take(pageSize).ToArray(); - throw; - } - } + if (pageOfIds.Length > 0) + { + await transaction.Connection.ExecuteAsync(deleteCmd, new { Ids = pageOfIds }, transaction); + ids = ids.Skip(pageSize).ToArray(); + } + } while (ids.Length > 0); - public async Task> GetIndexingTasksAsync(long afterTaskId, int count) + var insertCmd = $"insert into {dialect.QuoteForTableName(table, _store.Configuration.Schema)} ({dialect.QuoteForColumnName("CreatedUtc")}, {dialect.QuoteForColumnName("ContentItemId")}, {dialect.QuoteForColumnName("Type")}) values (@CreatedUtc, @ContentItemId, @Type);"; + await transaction.Connection.ExecuteAsync(insertCmd, localQueue, transaction); + + await transaction.CommitAsync(); + } + catch (Exception e) { - await using var connection = _dbConnectionAccessor.CreateConnection(); - await connection.OpenAsync(); + await transaction.RollbackAsync(); + logger.LogError(e, "An error occurred while updating indexing tasks"); - try - { - var dialect = _store.Configuration.SqlDialect; - var sqlBuilder = dialect.CreateBuilder(_store.Configuration.TablePrefix); + throw; + } + } - sqlBuilder.Select(); - sqlBuilder.Table(nameof(IndexingTask), alias: null, _store.Configuration.Schema); - sqlBuilder.Selector("*"); + public async Task> GetIndexingTasksAsync(long afterTaskId, int count) + { + await using var connection = _dbConnectionAccessor.CreateConnection(); + await connection.OpenAsync(); - if (count > 0) - { - sqlBuilder.Take(count.ToString()); - } + try + { + var dialect = _store.Configuration.SqlDialect; + var sqlBuilder = dialect.CreateBuilder(_store.Configuration.TablePrefix); - sqlBuilder.WhereAnd($"{dialect.QuoteForColumnName("Id")} > @Id"); - sqlBuilder.OrderBy($"{dialect.QuoteForColumnName("Id")}"); + sqlBuilder.Select(); + sqlBuilder.Table(nameof(IndexingTask), alias: null, _store.Configuration.Schema); + sqlBuilder.Selector("*"); - return await connection.QueryAsync(sqlBuilder.ToSqlString(), new { Id = afterTaskId }); - } - catch (Exception e) + if (count > 0) { - _logger.LogError(e, "An error occurred while reading indexing tasks"); - throw; + sqlBuilder.Take(count.ToString()); } + + sqlBuilder.WhereAnd($"{dialect.QuoteForColumnName("Id")} > @Id"); + sqlBuilder.OrderBy($"{dialect.QuoteForColumnName("Id")}"); + + return await connection.QueryAsync(sqlBuilder.ToSqlString(), new { Id = afterTaskId }); + } + catch (Exception e) + { + _logger.LogError(e, "An error occurred while reading indexing tasks"); + throw; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Indexing/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Indexing/Migrations.cs index 35aa91ae942..9aebd428970 100644 --- a/src/OrchardCore.Modules/OrchardCore.Indexing/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Indexing/Migrations.cs @@ -2,24 +2,23 @@ using System.Threading.Tasks; using OrchardCore.Data.Migration; -namespace OrchardCore.Indexing +namespace OrchardCore.Indexing; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + public async Task CreateAsync() { - public async Task CreateAsync() - { - await SchemaBuilder.CreateTableAsync(nameof(IndexingTask), table => table - .Column("Id", col => col.PrimaryKey().Identity()) - .Column("ContentItemId", c => c.WithLength(26)) - .Column("CreatedUtc", col => col.NotNull()) - .Column("Type") - ); + await SchemaBuilder.CreateTableAsync(nameof(IndexingTask), table => table + .Column("Id", col => col.PrimaryKey().Identity()) + .Column("ContentItemId", c => c.WithLength(26)) + .Column("CreatedUtc", col => col.NotNull()) + .Column("Type") + ); - await SchemaBuilder.AlterTableAsync(nameof(IndexingTask), table => table - .CreateIndex("IDX_IndexingTask_ContentItemId", "ContentItemId") - ); + await SchemaBuilder.AlterTableAsync(nameof(IndexingTask), table => table + .CreateIndex("IDX_IndexingTask_ContentItemId", "ContentItemId") + ); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Indexing/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Indexing/Startup.cs index e244b55efef..555f883ec4d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Indexing/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Indexing/Startup.cs @@ -4,18 +4,17 @@ using OrchardCore.Indexing.Services; using OrchardCore.Modules; -namespace OrchardCore.Indexing +namespace OrchardCore.Indexing; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddDataMigration(); - } + services.AddScoped(); + services.AddScoped(); + services.AddDataMigration(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Layers/AdminMenu.cs index 5ac1f743e95..0b3668be0ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/AdminMenu.cs @@ -4,47 +4,46 @@ using OrchardCore.Layers.Drivers; using OrchardCore.Navigation; -namespace OrchardCore.Layers +namespace OrchardCore.Layers; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", LayerSiteSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", LayerSiteSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Design"], design => design - .Add(S["Settings"], settings => settings - .Add(S["Zones"], S["Zones"].PrefixPosition(), zones => zones - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageLayers) - .LocalNav() - ) - ) - .Add(S["Widgets"], S["Widgets"].PrefixPosition(), widgets => widgets + builder + .Add(S["Design"], design => design + .Add(S["Settings"], settings => settings + .Add(S["Zones"], S["Zones"].PrefixPosition(), zones => zones + .Action("Index", "Admin", _routeValues) .Permission(Permissions.ManageLayers) - .Action("Index", "Admin", "OrchardCore.Layers") .LocalNav() ) - ); + ) + .Add(S["Widgets"], S["Widgets"].PrefixPosition(), widgets => widgets + .Permission(Permissions.ManageLayers) + .Action("Index", "Admin", "OrchardCore.Layers") + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/AdminController.cs index 98df4a0f18e..d1b2d49d48d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/AdminController.cs @@ -25,343 +25,342 @@ using OrchardCore.Settings; using YesSql; -namespace OrchardCore.Layers.Controllers +namespace OrchardCore.Layers.Controllers; + +[Admin("Layers/{action}/{id?}", "Layers.{action}")] +public class AdminController : Controller { - [Admin("Layers/{action}/{id?}", "Layers.{action}")] - public class AdminController : Controller + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly ISiteService _siteService; + private readonly ILayerService _layerService; + private readonly IAuthorizationService _authorizationService; + private readonly ISession _session; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IVolatileDocumentManager _layerStateManager; + private readonly IDisplayManager _conditionDisplayManager; + private readonly IDisplayManager _ruleDisplayManager; + private readonly IConditionIdGenerator _conditionIdGenerator; + private readonly IEnumerable _conditionFactories; + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + public AdminController( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + IContentItemDisplayManager contentItemDisplayManager, + ISiteService siteService, + ILayerService layerService, + IAuthorizationService authorizationService, + ISession session, + IUpdateModelAccessor updateModelAccessor, + IVolatileDocumentManager layerStateManager, + IDisplayManager conditionDisplayManager, + IDisplayManager ruleDisplayManager, + IConditionIdGenerator conditionIdGenerator, + IEnumerable conditionFactories, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + ILogger logger) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly ISiteService _siteService; - private readonly ILayerService _layerService; - private readonly IAuthorizationService _authorizationService; - private readonly ISession _session; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IVolatileDocumentManager _layerStateManager; - private readonly IDisplayManager _conditionDisplayManager; - private readonly IDisplayManager _ruleDisplayManager; - private readonly IConditionIdGenerator _conditionIdGenerator; - private readonly IEnumerable _conditionFactories; - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - public AdminController( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - IContentItemDisplayManager contentItemDisplayManager, - ISiteService siteService, - ILayerService layerService, - IAuthorizationService authorizationService, - ISession session, - IUpdateModelAccessor updateModelAccessor, - IVolatileDocumentManager layerStateManager, - IDisplayManager conditionDisplayManager, - IDisplayManager ruleDisplayManager, - IConditionIdGenerator conditionIdGenerator, - IEnumerable conditionFactories, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - ILogger logger) - { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _contentItemDisplayManager = contentItemDisplayManager; - _siteService = siteService; - _layerService = layerService; - _authorizationService = authorizationService; - _session = session; - _updateModelAccessor = updateModelAccessor; - _layerStateManager = layerStateManager; - _conditionDisplayManager = conditionDisplayManager; - _ruleDisplayManager = ruleDisplayManager; - _conditionIdGenerator = conditionIdGenerator; - _conditionFactories = conditionFactories; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; - _logger = logger; - } + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _contentItemDisplayManager = contentItemDisplayManager; + _siteService = siteService; + _layerService = layerService; + _authorizationService = authorizationService; + _session = session; + _updateModelAccessor = updateModelAccessor; + _layerStateManager = layerStateManager; + _conditionDisplayManager = conditionDisplayManager; + _ruleDisplayManager = ruleDisplayManager; + _conditionIdGenerator = conditionIdGenerator; + _conditionFactories = conditionFactories; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + _logger = logger; + } - [Admin("Layers", "Layers.Index")] - public async Task Index() + [Admin("Layers", "Layers.Index")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } - - var layers = await _layerService.GetLayersAsync(); - var widgets = await _layerService.GetLayerWidgetsMetadataAsync(c => c.Latest == true); - - var model = new LayersIndexViewModel { Layers = layers.Layers.ToList() }; - - var contentDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + return Forbid(); + } - model.Zones = (await _siteService.GetSettingsAsync()).Zones ?? []; - model.Widgets = []; + var layers = await _layerService.GetLayersAsync(); + var widgets = await _layerService.GetLayerWidgetsMetadataAsync(c => c.Latest == true); - foreach (var widget in widgets.OrderBy(x => x.Position)) - { - var zone = widget.Zone; - List list; - if (!model.Widgets.TryGetValue(zone, out list)) - { - model.Widgets.Add(zone, list = []); - } + var model = new LayersIndexViewModel { Layers = layers.Layers.ToList() }; - if (contentDefinitions.Any(c => c.Name == widget.ContentItem.ContentType)) - { - list.Add(await _contentItemDisplayManager.BuildDisplayAsync(widget.ContentItem, _updateModelAccessor.ModelUpdater, "SummaryAdmin")); - } - else - { - _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType); - await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType]); - } - } + var contentDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); - return View(model); - } + model.Zones = (await _siteService.GetSettingsAsync()).Zones ?? []; + model.Widgets = []; - [HttpPost] - public async Task Index(LayersIndexViewModel model) + foreach (var widget in widgets.OrderBy(x => x.Position)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + var zone = widget.Zone; + List list; + if (!model.Widgets.TryGetValue(zone, out list)) { - return Forbid(); + model.Widgets.Add(zone, list = []); } - return RedirectToAction(nameof(Index)); - } - - public async Task Create() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + if (contentDefinitions.Any(c => c.Name == widget.ContentItem.ContentType)) { - return Forbid(); + list.Add(await _contentItemDisplayManager.BuildDisplayAsync(widget.ContentItem, _updateModelAccessor.ModelUpdater, "SummaryAdmin")); } - - return View(); - } - - [HttpPost, ActionName("Create")] - public async Task CreatePost(LayerEditViewModel model) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + else { - return Forbid(); + _logger.LogWarning("The Widget content item with id {ContentItemId} has no matching {ContentType} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType); + await _notifier.WarningAsync(H["The Widget content item with id {0} has no matching {1} content type definition.", widget.ContentItem.ContentItemId, widget.ContentItem.ContentType]); } + } - var layers = await _layerService.LoadLayersAsync(); - - ValidateViewModel(model, layers, isNew: true); - - if (ModelState.IsValid) - { - var layer = new Layer - { - Name = model.Name, - Description = model.Description, - LayerRule = new Rule(), - }; - - _conditionIdGenerator.GenerateUniqueId(layer.LayerRule); - - layers.Layers.Add(layer); + return View(model); + } - await _layerService.UpdateAsync(layers); + [HttpPost] + public async Task Index(LayersIndexViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); + } - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); + } - return View(model); + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); } - public async Task Edit(string name) + return View(); + } + + [HttpPost, ActionName("Create")] + public async Task CreatePost(LayerEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + return Forbid(); + } - var layers = await _layerService.GetLayersAsync(); + var layers = await _layerService.LoadLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + ValidateViewModel(model, layers, isNew: true); - if (layer == null) + if (ModelState.IsValid) + { + var layer = new Layer { - return NotFound(); - } + Name = model.Name, + Description = model.Description, + LayerRule = new Rule(), + }; - var rule = await _ruleDisplayManager.BuildDisplayAsync(layer.LayerRule, _updateModelAccessor.ModelUpdater, "Summary"); - rule.Properties["ConditionId"] = layer.LayerRule.ConditionId; + _conditionIdGenerator.GenerateUniqueId(layer.LayerRule); - var thumbnails = new Dictionary(); - foreach (var factory in _conditionFactories) - { - var condition = factory.Create(); - var thumbnail = await _conditionDisplayManager.BuildDisplayAsync(condition, _updateModelAccessor.ModelUpdater, "Thumbnail"); - thumbnail.Properties["Condition"] = condition; - thumbnail.Properties["TargetUrl"] = Url.ActionLink("Create", "LayerRule", new { name, type = factory.Name }); - thumbnails.Add(factory.Name, thumbnail); - } + layers.Layers.Add(layer); - var model = new LayerEditViewModel - { - Name = layer.Name, - Description = layer.Description, - LayerRule = rule, - Thumbnails = thumbnails, - }; + await _layerService.UpdateAsync(layers); - return View(model); + return RedirectToAction(nameof(Index)); } - [HttpPost, ActionName("Edit")] - public async Task EditPost(LayerEditViewModel model) + return View(model); + } + + public async Task Edit(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + return Forbid(); + } - var layers = await _layerService.LoadLayersAsync(); + var layers = await _layerService.GetLayersAsync(); - ValidateViewModel(model, layers, isNew: false); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - if (ModelState.IsValid) - { - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); + if (layer == null) + { + return NotFound(); + } - if (layer == null) - { - return NotFound(); - } + var rule = await _ruleDisplayManager.BuildDisplayAsync(layer.LayerRule, _updateModelAccessor.ModelUpdater, "Summary"); + rule.Properties["ConditionId"] = layer.LayerRule.ConditionId; - layer.Name = model.Name; - layer.Description = model.Description; + var thumbnails = new Dictionary(); + foreach (var factory in _conditionFactories) + { + var condition = factory.Create(); + var thumbnail = await _conditionDisplayManager.BuildDisplayAsync(condition, _updateModelAccessor.ModelUpdater, "Thumbnail"); + thumbnail.Properties["Condition"] = condition; + thumbnail.Properties["TargetUrl"] = Url.ActionLink("Create", "LayerRule", new { name, type = factory.Name }); + thumbnails.Add(factory.Name, thumbnail); + } - await _layerService.UpdateAsync(layers); + var model = new LayerEditViewModel + { + Name = layer.Name, + Description = layer.Description, + LayerRule = rule, + Thumbnails = thumbnails, + }; - return RedirectToAction(nameof(Index)); - } + return View(model); + } - return View(model); + [HttpPost, ActionName("Edit")] + public async Task EditPost(LayerEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string name) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + var layers = await _layerService.LoadLayersAsync(); - var layers = await _layerService.LoadLayersAsync(); + ValidateViewModel(model, layers, isNew: false); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + if (ModelState.IsValid) + { + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); if (layer == null) { return NotFound(); } - var widgets = await _layerService.GetLayerWidgetsMetadataAsync(c => c.Latest == true); + layer.Name = model.Name; + layer.Description = model.Description; - if (!widgets.Any(x => string.Equals(x.Layer, name, StringComparison.OrdinalIgnoreCase))) - { - layers.Layers.Remove(layer); - await _layerService.UpdateAsync(layers); - await _notifier.SuccessAsync(H["Layer deleted successfully."]); - } - else - { - await _notifier.ErrorAsync(H["The layer couldn't be deleted: you must remove any associated widgets first."]); - } + await _layerService.UpdateAsync(layers); return RedirectToAction(nameof(Index)); } - [HttpPost] - public async Task UpdatePosition(string contentItemId, double position, string zone) + return View(model); + } + + [HttpPost] + public async Task Delete(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Unauthorized(); - } + return Forbid(); + } - // Load the latest version first if any - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); + var layers = await _layerService.LoadLayersAsync(); - if (contentItem == null) - { - return NotFound(); - } + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - var layerMetadata = contentItem.As(); + if (layer == null) + { + return NotFound(); + } - if (layerMetadata == null) - { - return Forbid(); - } + var widgets = await _layerService.GetLayerWidgetsMetadataAsync(c => c.Latest == true); - layerMetadata.Position = position; - layerMetadata.Zone = zone; + if (!widgets.Any(x => string.Equals(x.Layer, name, StringComparison.OrdinalIgnoreCase))) + { + layers.Layers.Remove(layer); + await _layerService.UpdateAsync(layers); + await _notifier.SuccessAsync(H["Layer deleted successfully."]); + } + else + { + await _notifier.ErrorAsync(H["The layer couldn't be deleted: you must remove any associated widgets first."]); + } - contentItem.Apply(layerMetadata); + return RedirectToAction(nameof(Index)); + } - await _session.SaveAsync(contentItem); + [HttpPost] + public async Task UpdatePosition(string contentItemId, double position, string zone) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Unauthorized(); + } - // In case the moved contentItem is the draft for a published contentItem we update it's position too. - // We do that because we want the position of published and draft version to be the same. - if (contentItem.IsPublished() == false) - { - var publishedContentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Published); - if (publishedContentItem != null) - { - layerMetadata = contentItem.As(); + // Load the latest version first if any + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Latest); - if (layerMetadata == null) - { - return Forbid(); - } + if (contentItem == null) + { + return NotFound(); + } - layerMetadata.Position = position; - layerMetadata.Zone = zone; + var layerMetadata = contentItem.As(); - publishedContentItem.Apply(layerMetadata); + if (layerMetadata == null) + { + return Forbid(); + } - await _session.SaveAsync(publishedContentItem); - } - } + layerMetadata.Position = position; + layerMetadata.Zone = zone; - // The state will be updated once the ambient session is committed. - await _layerStateManager.UpdateAsync(new LayerState()); + contentItem.Apply(layerMetadata); - if (Request.Headers != null && Request.Headers.XRequestedWith == "XMLHttpRequest") - { - return Ok(); - } - else + await _session.SaveAsync(contentItem); + + // In case the moved contentItem is the draft for a published contentItem we update it's position too. + // We do that because we want the position of published and draft version to be the same. + if (contentItem.IsPublished() == false) + { + var publishedContentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Published); + if (publishedContentItem != null) { - return RedirectToAction(nameof(Index)); + layerMetadata = contentItem.As(); + + if (layerMetadata == null) + { + return Forbid(); + } + + layerMetadata.Position = position; + layerMetadata.Zone = zone; + + publishedContentItem.Apply(layerMetadata); + + await _session.SaveAsync(publishedContentItem); } } - private void ValidateViewModel(LayerEditViewModel model, LayersDocument layers, bool isNew) + // The state will be updated once the ambient session is committed. + await _layerStateManager.UpdateAsync(new LayerState()); + + if (Request.Headers != null && Request.Headers.XRequestedWith == "XMLHttpRequest") { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(LayerEditViewModel.Name), S["The layer name is required."]); - } - else if (isNew && layers.Layers.Any(x => string.Equals(x.Name, model.Name, StringComparison.OrdinalIgnoreCase))) - { - ModelState.AddModelError(nameof(LayerEditViewModel.Name), S["The layer name already exists."]); - } + return Ok(); + } + else + { + return RedirectToAction(nameof(Index)); + } + } + + private void ValidateViewModel(LayerEditViewModel model, LayersDocument layers, bool isNew) + { + if (string.IsNullOrWhiteSpace(model.Name)) + { + ModelState.AddModelError(nameof(LayerEditViewModel.Name), S["The layer name is required."]); + } + else if (isNew && layers.Layers.Any(x => string.Equals(x.Name, model.Name, StringComparison.OrdinalIgnoreCase))) + { + ModelState.AddModelError(nameof(LayerEditViewModel.Name), S["The layer name already exists."]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/LayerRuleController.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/LayerRuleController.cs index e502f305125..dbd3ba81a96 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/LayerRuleController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Controllers/LayerRuleController.cs @@ -14,346 +14,345 @@ using OrchardCore.Rules; using OrchardCore.Rules.Services; -namespace OrchardCore.Layers.Controllers +namespace OrchardCore.Layers.Controllers; + +[Admin("Layers/Rules/{action}", "Layers.Rules.{action}")] +public class LayerRuleController : Controller { - [Admin("Layers/Rules/{action}", "Layers.Rules.{action}")] - public class LayerRuleController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _factories; + private readonly ILayerService _layerService; + private readonly IConditionIdGenerator _conditionIdGenerator; + private readonly INotifier _notifier; + private readonly IUpdateModelAccessor _updateModelAccessor; + + protected readonly IHtmlLocalizer H; + + public LayerRuleController( + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable factories, + ILayerService layerService, + IConditionIdGenerator conditionIdGenerator, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IUpdateModelAccessor updateModelAccessor) { - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _factories; - private readonly ILayerService _layerService; - private readonly IConditionIdGenerator _conditionIdGenerator; - private readonly INotifier _notifier; - private readonly IUpdateModelAccessor _updateModelAccessor; - - protected readonly IHtmlLocalizer H; - - public LayerRuleController( - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable factories, - ILayerService layerService, - IConditionIdGenerator conditionIdGenerator, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IUpdateModelAccessor updateModelAccessor) - { - _displayManager = displayManager; - _factories = factories; - _authorizationService = authorizationService; - _layerService = layerService; - _conditionIdGenerator = conditionIdGenerator; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - H = htmlLocalizer; - } + _displayManager = displayManager; + _factories = factories; + _authorizationService = authorizationService; + _layerService = layerService; + _conditionIdGenerator = conditionIdGenerator; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + H = htmlLocalizer; + } - public async Task Create(string name, string type, string conditionGroupId) + public async Task Create(string name, string type, string conditionGroupId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } - - var layers = await _layerService.GetLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - - if (layer == null) - { - return NotFound(); - } + return Forbid(); + } - var conditionGroup = FindConditionGroup(layer.LayerRule, conditionGroupId); + var layers = await _layerService.GetLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - if (conditionGroup == null) - { - return NotFound(); - } + if (layer == null) + { + return NotFound(); + } - var condition = _factories.FirstOrDefault(x => x.Name == type)?.Create(); + var conditionGroup = FindConditionGroup(layer.LayerRule, conditionGroupId); - if (condition == null) - { - return NotFound(); - } + if (conditionGroup == null) + { + return NotFound(); + } - var model = new LayerRuleCreateViewModel - { - Name = name, - ConditionGroupId = conditionGroup.ConditionId, - ConditionType = type, - Editor = await _displayManager.BuildEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty) - }; + var condition = _factories.FirstOrDefault(x => x.Name == type)?.Create(); - return View(model); + if (condition == null) + { + return NotFound(); } - [HttpPost] - public async Task Create(LayerRuleCreateViewModel model) + var model = new LayerRuleCreateViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + Name = name, + ConditionGroupId = conditionGroup.ConditionId, + ConditionType = type, + Editor = await _displayManager.BuildEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: true, string.Empty, string.Empty) + }; - var layers = await _layerService.LoadLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); + return View(model); + } - if (layer == null) - { - return NotFound(); - } + [HttpPost] + public async Task Create(LayerRuleCreateViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); + } - var conditionGroup = FindConditionGroup(layer.LayerRule, model.ConditionGroupId); + var layers = await _layerService.LoadLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); - if (conditionGroup == null) - { - return NotFound(); - } + if (layer == null) + { + return NotFound(); + } - var condition = _factories.FirstOrDefault(x => x.Name == model.ConditionType)?.Create(); + var conditionGroup = FindConditionGroup(layer.LayerRule, model.ConditionGroupId); - if (condition == null) - { - return NotFound(); - } + if (conditionGroup == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); + var condition = _factories.FirstOrDefault(x => x.Name == model.ConditionType)?.Create(); - if (ModelState.IsValid) - { - _conditionIdGenerator.GenerateUniqueId(condition); - conditionGroup.Conditions.Add(condition); - await _layerService.UpdateAsync(layers); - await _notifier.SuccessAsync(H["Condition added successfully."]); - return RedirectToAction(nameof(Edit), "Admin", new { name = model.Name }); - } + if (condition == null) + { + return NotFound(); + } - model.Editor = editor; + var editor = await _displayManager.UpdateEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); - // If we got this far, something failed, redisplay form. - return View(model); + if (ModelState.IsValid) + { + _conditionIdGenerator.GenerateUniqueId(condition); + conditionGroup.Conditions.Add(condition); + await _layerService.UpdateAsync(layers); + await _notifier.SuccessAsync(H["Condition added successfully."]); + return RedirectToAction(nameof(Edit), "Admin", new { name = model.Name }); } - public async Task Edit(string name, string conditionId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + model.Editor = editor; - var layers = await _layerService.GetLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + // If we got this far, something failed, redisplay form. + return View(model); + } - if (layer == null) - { - return NotFound(); - } + public async Task Edit(string name, string conditionId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); + } - var condition = FindCondition(layer.LayerRule, conditionId); + var layers = await _layerService.GetLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - if (condition == null) - { - return NotFound(); - } + if (layer == null) + { + return NotFound(); + } - var model = new LayerRuleEditViewModel - { - Name = name, - Editor = await _displayManager.BuildEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") - }; + var condition = FindCondition(layer.LayerRule, conditionId); - return View(model); + if (condition == null) + { + return NotFound(); } - [HttpPost] - public async Task Edit(LayerRuleEditViewModel model) + var model = new LayerRuleEditViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + Name = name, + Editor = await _displayManager.BuildEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") + }; - var layers = await _layerService.LoadLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); + return View(model); + } - if (layer == null) - { - return NotFound(); - } + [HttpPost] + public async Task Edit(LayerRuleEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); + } - var condition = FindCondition(layer.LayerRule, model.ConditionId); + var layers = await _layerService.LoadLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, model.Name, StringComparison.Ordinal)); - if (condition == null) - { - return NotFound(); - } + if (layer == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var condition = FindCondition(layer.LayerRule, model.ConditionId); - if (ModelState.IsValid) - { - await _layerService.UpdateAsync(layers); - await _notifier.SuccessAsync(H["Condition updated successfully."]); - return RedirectToAction(nameof(Edit), "Admin", new { name = model.Name }); - } + if (condition == null) + { + return NotFound(); + } - await _notifier.ErrorAsync(H["The condition has validation errors."]); - model.Editor = editor; + var editor = await _displayManager.UpdateEditorAsync(condition, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - // If we got this far, something failed, redisplay form. - return View(model); + if (ModelState.IsValid) + { + await _layerService.UpdateAsync(layers); + await _notifier.SuccessAsync(H["Condition updated successfully."]); + return RedirectToAction(nameof(Edit), "Admin", new { name = model.Name }); } - [HttpPost] - public async Task Delete(string name, string conditionId) + await _notifier.ErrorAsync(H["The condition has validation errors."]); + model.Editor = editor; + + // If we got this far, something failed, redisplay form. + return View(model); + } + + [HttpPost] + public async Task Delete(string name, string conditionId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + return Forbid(); + } - var layers = await _layerService.LoadLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + var layers = await _layerService.LoadLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); - if (layer == null) - { - return NotFound(); - } + if (layer == null) + { + return NotFound(); + } - var condition = FindCondition(layer.LayerRule, conditionId); - var conditionParent = FindConditionParent(layer.LayerRule, conditionId); + var condition = FindCondition(layer.LayerRule, conditionId); + var conditionParent = FindConditionParent(layer.LayerRule, conditionId); - if (condition == null || conditionParent == null) - { - return NotFound(); - } + if (condition == null || conditionParent == null) + { + return NotFound(); + } - conditionParent.Conditions.Remove(condition); - await _layerService.UpdateAsync(layers); + conditionParent.Conditions.Remove(condition); + await _layerService.UpdateAsync(layers); + + await _notifier.SuccessAsync(H["Condition deleted successfully."]); - await _notifier.SuccessAsync(H["Condition deleted successfully."]); + return RedirectToAction(nameof(Edit), "Admin", new { name }); + } - return RedirectToAction(nameof(Edit), "Admin", new { name }); + [Admin("Layers/Rules/Order", "Layers.Rules.Order")] + public async Task UpdateOrder(string name, string conditionId, string toConditionId, int toPosition) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) + { + return Forbid(); } - [Admin("Layers/Rules/Order", "Layers.Rules.Order")] - public async Task UpdateOrder(string name, string conditionId, string toConditionId, int toPosition) + var layers = await _layerService.LoadLayersAsync(); + var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + + if (layer == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLayers)) - { - return Forbid(); - } + return NotFound(); + } - var layers = await _layerService.LoadLayersAsync(); - var layer = layers.Layers.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.Ordinal)); + var condition = FindCondition(layer.LayerRule, conditionId); + var conditionParent = FindConditionParent(layer.LayerRule, conditionId); + var toCondition = FindCondition(layer.LayerRule, toConditionId); - if (layer == null) - { - return NotFound(); - } + if (condition == null || conditionParent == null || toCondition == null || toCondition is not ConditionGroup toGroupCondition) + { + return NotFound(); + } - var condition = FindCondition(layer.LayerRule, conditionId); - var conditionParent = FindConditionParent(layer.LayerRule, conditionId); - var toCondition = FindCondition(layer.LayerRule, toConditionId); + conditionParent.Conditions.Remove(condition); + toGroupCondition.Conditions.Insert(toPosition, condition); - if (condition == null || conditionParent == null || toCondition == null || toCondition is not ConditionGroup toGroupCondition) - { - return NotFound(); - } + await _layerService.UpdateAsync(layers); - conditionParent.Conditions.Remove(condition); - toGroupCondition.Conditions.Insert(toPosition, condition); + return Ok(); + } - await _layerService.UpdateAsync(layers); + private static Condition FindCondition(Condition condition, string conditionId) + { + if (string.Equals(condition.ConditionId, conditionId, StringComparison.OrdinalIgnoreCase)) + { + return condition; + } - return Ok(); + if (condition is not ConditionGroup conditionGroup) + { + return null; } - private static Condition FindCondition(Condition condition, string conditionId) + if (conditionGroup.Conditions.Count == 0) { - if (string.Equals(condition.ConditionId, conditionId, StringComparison.OrdinalIgnoreCase)) - { - return condition; - } + return null; + } - if (condition is not ConditionGroup conditionGroup) - { - return null; - } + Condition result; + + foreach (var nestedCondition in conditionGroup.Conditions) + { + // Search in inner conditions. + result = FindCondition(nestedCondition, conditionId); - if (conditionGroup.Conditions.Count == 0) + if (result != null) { - return null; + return result; } + } - Condition result; - - foreach (var nestedCondition in conditionGroup.Conditions) - { - // Search in inner conditions. - result = FindCondition(nestedCondition, conditionId); + return null; + } - if (result != null) - { - return result; - } - } + private static ConditionGroup FindConditionGroup(ConditionGroup condition, string groupConditionId) + { + if (string.Equals(condition.ConditionId, groupConditionId, StringComparison.OrdinalIgnoreCase)) + { + return condition; + } + if (condition.Conditions.Count == 0) + { return null; } - private static ConditionGroup FindConditionGroup(ConditionGroup condition, string groupConditionId) + ConditionGroup result; + + foreach (var nestedCondition in condition.Conditions.OfType()) { - if (string.Equals(condition.ConditionId, groupConditionId, StringComparison.OrdinalIgnoreCase)) - { - return condition; - } + // Search in inner conditions. + result = FindConditionGroup(nestedCondition, groupConditionId); - if (condition.Conditions.Count == 0) + if (result != null) { - return null; + return result; } + } + + return null; + } - ConditionGroup result; + private static ConditionGroup FindConditionParent(ConditionGroup condition, string conditionId) + { + ConditionGroup result; - foreach (var nestedCondition in condition.Conditions.OfType()) + foreach (var nestedCondition in condition.Conditions) + { + if (string.Equals(nestedCondition.ConditionId, conditionId, StringComparison.OrdinalIgnoreCase)) { - // Search in inner conditions. - result = FindConditionGroup(nestedCondition, groupConditionId); + return condition; + } + if (nestedCondition is ConditionGroup nestedConditionGroup) + { + result = FindConditionParent(nestedConditionGroup, conditionId); if (result != null) { return result; } } - - return null; } - private static ConditionGroup FindConditionParent(ConditionGroup condition, string conditionId) - { - ConditionGroup result; - - foreach (var nestedCondition in condition.Conditions) - { - if (string.Equals(nestedCondition.ConditionId, conditionId, StringComparison.OrdinalIgnoreCase)) - { - return condition; - } - - if (nestedCondition is ConditionGroup nestedConditionGroup) - { - result = FindConditionParent(nestedConditionGroup, conditionId); - if (result != null) - { - return result; - } - } - } - - return null; - } + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentSource.cs index 74f4bbaea4d..64b256b5ccd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentSource.cs @@ -8,47 +8,46 @@ using OrchardCore.Layers.Services; using OrchardCore.Settings; -namespace OrchardCore.Layers.Deployment +namespace OrchardCore.Layers.Deployment; + +public class AllLayersDeploymentSource : IDeploymentSource { - public class AllLayersDeploymentSource : IDeploymentSource + private readonly ILayerService _layerService; + private readonly ISiteService _siteService; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public AllLayersDeploymentSource( + ILayerService layerService, + ISiteService siteService, + IOptions serializationOptions) + { + _layerService = layerService; + _siteService = siteService; + _jsonSerializerOptions = serializationOptions.Value.SerializerOptions; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly ILayerService _layerService; - private readonly ISiteService _siteService; - private readonly JsonSerializerOptions _jsonSerializerOptions; - - public AllLayersDeploymentSource( - ILayerService layerService, - ISiteService siteService, - IOptions serializationOptions) + if (step is not AllLayersDeploymentStep) { - _layerService = layerService; - _siteService = siteService; - _jsonSerializerOptions = serializationOptions.Value.SerializerOptions; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var layers = await _layerService.GetLayersAsync(); + + result.Steps.Add(new JsonObject { - if (step is not AllLayersDeploymentStep) - { - return; - } - - var layers = await _layerService.GetLayersAsync(); - - result.Steps.Add(new JsonObject - { - ["name"] = "Layers", - ["Layers"] = JArray.FromObject(layers.Layers, _jsonSerializerOptions), - }); - - var layerSettings = await _siteService.GetSettingsAsync(); - - // Adding Layer settings - result.Steps.Add(new JsonObject - { - ["name"] = "Settings", - ["LayerSettings"] = JObject.FromObject(layerSettings), - }); - } + ["name"] = "Layers", + ["Layers"] = JArray.FromObject(layers.Layers, _jsonSerializerOptions), + }); + + var layerSettings = await _siteService.GetSettingsAsync(); + + // Adding Layer settings + result.Steps.Add(new JsonObject + { + ["name"] = "Settings", + ["LayerSettings"] = JObject.FromObject(layerSettings), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStep.cs index 24245a7cc4f..9027ec0790f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Layers.Deployment +namespace OrchardCore.Layers.Deployment; + +/// +/// Adds layers to a . +/// +public class AllLayersDeploymentStep : DeploymentStep { - /// - /// Adds layers to a . - /// - public class AllLayersDeploymentStep : DeploymentStep + public AllLayersDeploymentStep() { - public AllLayersDeploymentStep() - { - Name = "AllLayers"; - } + Name = "AllLayers"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStepDriver.cs index 9df14469239..ea5c985efca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Deployment/AllLayersDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Layers.Deployment +namespace OrchardCore.Layers.Deployment; + +public sealed class AllLayersDeploymentStepDriver : DisplayDriver { - public sealed class AllLayersDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllLayersDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllLayersDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllLayersDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllLayersDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllLayersDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllLayersDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllLayersDeploymentStep step, BuildEditorContext context) - { - return View("AllLayersDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllLayersDeploymentStep step, BuildEditorContext context) + { + return View("AllLayersDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerMetadataWelder.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerMetadataWelder.cs index eebbf7a4963..0f8786e1031 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerMetadataWelder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerMetadataWelder.cs @@ -9,86 +9,85 @@ using OrchardCore.Layers.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Layers.Drivers +namespace OrchardCore.Layers.Drivers; + +public class LayerMetadataWelder : ContentDisplayDriver { - public class LayerMetadataWelder : ContentDisplayDriver + private readonly ILayerService _layerService; + protected readonly IStringLocalizer S; + + public LayerMetadataWelder(ILayerService layerService, IStringLocalizer stringLocalizer) { - private readonly ILayerService _layerService; - protected readonly IStringLocalizer S; + _layerService = layerService; + S = stringLocalizer; + } - public LayerMetadataWelder(ILayerService layerService, IStringLocalizer stringLocalizer) + protected override void BuildPrefix(ContentItem model, string htmlFieldPrefix) + { + base.BuildPrefix(model, htmlFieldPrefix); + if (string.IsNullOrWhiteSpace(htmlFieldPrefix)) { - _layerService = layerService; - S = stringLocalizer; + Prefix = "LayerMetadata"; } + } - protected override void BuildPrefix(ContentItem model, string htmlFieldPrefix) - { - base.BuildPrefix(model, htmlFieldPrefix); - if (string.IsNullOrWhiteSpace(htmlFieldPrefix)) - { - Prefix = "LayerMetadata"; - } - } + public override async Task EditAsync(ContentItem model, BuildEditorContext context) + { + var layerMetadata = model.As(); - public override async Task EditAsync(ContentItem model, BuildEditorContext context) + if (layerMetadata == null) { - var layerMetadata = model.As(); + layerMetadata = new LayerMetadata(); + await context.Updater.TryUpdateModelAsync(layerMetadata, Prefix, m => m.Zone, m => m.Position); - if (layerMetadata == null) + // Are we loading an editor that requires layer metadata? + if (!string.IsNullOrEmpty(layerMetadata.Zone)) { - layerMetadata = new LayerMetadata(); - await context.Updater.TryUpdateModelAsync(layerMetadata, Prefix, m => m.Zone, m => m.Position); - - // Are we loading an editor that requires layer metadata? - if (!string.IsNullOrEmpty(layerMetadata.Zone)) - { - model.Weld(layerMetadata); - } - else - { - return null; - } + model.Weld(layerMetadata); } - - return Initialize("LayerMetadata_Edit", async shape => + else { - shape.Title = model.DisplayText; - shape.LayerMetadata = layerMetadata; - shape.Layers = (await _layerService.GetLayersAsync()).Layers; - }) - .Location("Content:before"); + return null; + } } - public override async Task UpdateAsync(ContentItem model, UpdateEditorContext context) + return Initialize("LayerMetadata_Edit", async shape => { - var viewModel = new LayerMetadataEditViewModel(); - - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + shape.Title = model.DisplayText; + shape.LayerMetadata = layerMetadata; + shape.Layers = (await _layerService.GetLayersAsync()).Layers; + }) + .Location("Content:before"); + } - if (viewModel.LayerMetadata == null) - { - return null; - } + public override async Task UpdateAsync(ContentItem model, UpdateEditorContext context) + { + var viewModel = new LayerMetadataEditViewModel(); - if (string.IsNullOrEmpty(viewModel.LayerMetadata.Zone)) - { - context.Updater.ModelState.AddModelError(Prefix, "LayerMetadata.Zone", S["Zone is missing"]); - } + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - if (string.IsNullOrEmpty(viewModel.LayerMetadata.Layer)) - { - context.Updater.ModelState.AddModelError(Prefix, "LayerMetadata.Layer", S["Layer is missing"]); - } + if (viewModel.LayerMetadata == null) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - model.Apply(viewModel.LayerMetadata); - } + if (string.IsNullOrEmpty(viewModel.LayerMetadata.Zone)) + { + context.Updater.ModelState.AddModelError(Prefix, "LayerMetadata.Zone", S["Zone is missing"]); + } - model.DisplayText = viewModel.Title; + if (string.IsNullOrEmpty(viewModel.LayerMetadata.Layer)) + { + context.Updater.ModelState.AddModelError(Prefix, "LayerMetadata.Layer", S["Layer is missing"]); + } - return await EditAsync(model, context); + if (context.Updater.ModelState.IsValid) + { + model.Apply(viewModel.LayerMetadata); } + + model.DisplayText = viewModel.Title; + + return await EditAsync(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerSiteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerSiteSettingsDisplayDriver.cs index fac6ce8976f..0385766f8cb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerSiteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Drivers/LayerSiteSettingsDisplayDriver.cs @@ -9,60 +9,59 @@ using OrchardCore.Layers.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Layers.Drivers +namespace OrchardCore.Layers.Drivers; + +public sealed class LayerSiteSettingsDisplayDriver : SiteDisplayDriver { - public sealed class LayerSiteSettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "zones"; + + private static readonly char[] _separator = [' ', ',']; + + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public LayerSiteSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - public const string GroupId = "zones"; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - private static readonly char[] _separator = [' ', ',']; + protected override string SettingsGroupId + => GroupId; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + public override async Task EditAsync(ISite site, LayerSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public LayerSiteSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLayers)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => GroupId; - - public override async Task EditAsync(ISite site, LayerSettings settings, BuildEditorContext context) + return Initialize("LayerSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLayers)) - { - return null; - } + model.Zones = string.Join(", ", settings.Zones); + }).Location("Content:3") + .OnGroup(SettingsGroupId); + } - return Initialize("LayerSettings_Edit", model => - { - model.Zones = string.Join(", ", settings.Zones); - }).Location("Content:3") - .OnGroup(SettingsGroupId); - } + public override async Task UpdateAsync(ISite site, LayerSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task UpdateAsync(ISite site, LayerSettings settings, UpdateEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLayers)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLayers)) - { - return null; - } + return null; + } - var model = new LayerSettingsViewModel(); + var model = new LayerSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.Zones = (model.Zones ?? string.Empty).Split(_separator, StringSplitOptions.RemoveEmptyEntries); + settings.Zones = (model.Zones ?? string.Empty).Split(_separator, StringSplitOptions.RemoveEmptyEntries); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs index a2d65e389a1..44ab2fde912 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerQueryObjectType.cs @@ -14,50 +14,49 @@ using OrchardCore.Layers.Services; using OrchardCore.Rules; -namespace OrchardCore.Layers.GraphQL +namespace OrchardCore.Layers.GraphQL; + +public class LayerQueryObjectType : ObjectGraphType { - public class LayerQueryObjectType : ObjectGraphType + public LayerQueryObjectType() { - public LayerQueryObjectType() - { - Name = "Layer"; - - Field(layer => layer.Name).Description("The name of the layer."); - Field, IEnumerable>("layerrule") - .Description("The rule that activates the layer.") - .Resolve(ctx => ctx.Source.LayerRule.Conditions); - Field(layer => layer.Description).Description("The description of the layer."); - Field, IEnumerable>("widgets") - .Description("The widgets for this layer.") - .Argument("status", "publication status of the widgets") - .ResolveLockedAsync(GetWidgetsForLayerAsync); - - async ValueTask> GetWidgetsForLayerAsync(IResolveFieldContext context) - { - var layerService = context.RequestServices.GetService(); + Name = "Layer"; - var filter = GetVersionFilter(context.GetArgument("status")); - var widgets = await layerService.GetLayerWidgetsAsync(filter); + Field(layer => layer.Name).Description("The name of the layer."); + Field, IEnumerable>("layerrule") + .Description("The rule that activates the layer.") + .Resolve(ctx => ctx.Source.LayerRule.Conditions); + Field(layer => layer.Description).Description("The description of the layer."); + Field, IEnumerable>("widgets") + .Description("The widgets for this layer.") + .Argument("status", "publication status of the widgets") + .ResolveLockedAsync(GetWidgetsForLayerAsync); - var layerWidgets = widgets?.Where(item => - { - var metadata = item.As(); - if (metadata == null) return false; - return metadata.Layer == context.Source.Name; - }); + async ValueTask> GetWidgetsForLayerAsync(IResolveFieldContext context) + { + var layerService = context.RequestServices.GetService(); - return layerWidgets; - } - } + var filter = GetVersionFilter(context.GetArgument("status")); + var widgets = await layerService.GetLayerWidgetsAsync(filter); - private static Expression> GetVersionFilter(PublicationStatusEnum status) => - status switch + var layerWidgets = widgets?.Where(item => { - PublicationStatusEnum.Published => x => x.Published, - PublicationStatusEnum.Draft => x => x.Latest && !x.Published, - PublicationStatusEnum.Latest => x => x.Latest, - PublicationStatusEnum.All => x => true, - _ => x => x.Published, - }; + var metadata = item.As(); + if (metadata == null) return false; + return metadata.Layer == context.Source.Name; + }); + + return layerWidgets; + } } + + private static Expression> GetVersionFilter(PublicationStatusEnum status) => + status switch + { + PublicationStatusEnum.Published => x => x.Published, + PublicationStatusEnum.Draft => x => x.Latest && !x.Published, + PublicationStatusEnum.Latest => x => x.Latest, + PublicationStatusEnum.All => x => true, + _ => x => x.Published, + }; } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs index ac252281451..93fe4be7ff4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/LayerWidgetQueryObjectType.cs @@ -3,29 +3,28 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Layers.Models; -namespace OrchardCore.Layers.GraphQL +namespace OrchardCore.Layers.GraphQL; + +public class LayerWidgetQueryObjectType : ObjectGraphType { - public class LayerWidgetQueryObjectType : ObjectGraphType + public LayerWidgetQueryObjectType() { - public LayerWidgetQueryObjectType() - { - Name = "LayerWidget"; + Name = "LayerWidget"; - Field("zone") - .Description("The name of the widget's zone.") - .Resolve(context => context.Source.As()?.Zone); + Field("zone") + .Description("The name of the widget's zone.") + .Resolve(context => context.Source.As()?.Zone); - Field("position") - .Description("The position of the widget in the zone.") - .Resolve(context => context.Source.As()?.Position); + Field("position") + .Description("The position of the widget in the zone.") + .Resolve(context => context.Source.As()?.Position); - Field("renderTitle") - .Description("Whether to render the widget's title.") - .Resolve(context => context.Source.As()?.RenderTitle); + Field("renderTitle") + .Description("Whether to render the widget's title.") + .Resolve(context => context.Source.As()?.RenderTitle); - Field("widget") - .Description("A widget on this layer.") - .Resolve(context => context.Source); - } + Field("widget") + .Description("A widget on this layer.") + .Resolve(context => context.Source); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/SiteLayersQuery.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/SiteLayersQuery.cs index 593b645cd27..d60f7e3315c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/SiteLayersQuery.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/SiteLayersQuery.cs @@ -11,48 +11,47 @@ using OrchardCore.Layers.Models; using OrchardCore.Layers.Services; -namespace OrchardCore.Layers.GraphQL +namespace OrchardCore.Layers.GraphQL; + +public class SiteLayersQuery : ISchemaBuilder { - public class SiteLayersQuery : ISchemaBuilder + protected readonly IStringLocalizer S; + private readonly GraphQLContentOptions _graphQLContentOptions; + + public SiteLayersQuery( + IStringLocalizer localizer, + IOptions graphQLContentOptions) { - protected readonly IStringLocalizer S; - private readonly GraphQLContentOptions _graphQLContentOptions; + S = localizer; + _graphQLContentOptions = graphQLContentOptions.Value; + } + + public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - public SiteLayersQuery( - IStringLocalizer localizer, - IOptions graphQLContentOptions) + public Task BuildAsync(ISchema schema) + { + if (_graphQLContentOptions.IsHiddenByDefault("SiteLayers")) { - S = localizer; - _graphQLContentOptions = graphQLContentOptions.Value; + return Task.CompletedTask; } - public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - - public Task BuildAsync(ISchema schema) + var field = new FieldType { - if (_graphQLContentOptions.IsHiddenByDefault("SiteLayers")) - { - return Task.CompletedTask; - } + Name = "SiteLayers", + Description = S["Site layers define the rules and zone placement for widgets."], + Type = typeof(ListGraphType), + Resolver = new LockedAsyncFieldResolver>(ResolveAsync) + }; - var field = new FieldType - { - Name = "SiteLayers", - Description = S["Site layers define the rules and zone placement for widgets."], - Type = typeof(ListGraphType), - Resolver = new LockedAsyncFieldResolver>(ResolveAsync) - }; + schema.Query.AddField(field); - schema.Query.AddField(field); - - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) - { - var layerService = resolveContext.RequestServices.GetService(); - var allLayers = await layerService.GetLayersAsync(); - return allLayers.Layers; - } + private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) + { + var layerService = resolveContext.RequestServices.GetService(); + var allLayers = await layerService.GetLayersAsync(); + return allLayers.Layers; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/Startup.cs index e0d85ad68b4..07705e07854 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/GraphQL/Startup.cs @@ -2,16 +2,15 @@ using OrchardCore.Apis.GraphQL; using OrchardCore.Modules; -namespace OrchardCore.Layers.GraphQL +namespace OrchardCore.Layers.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddTransient(); - services.AddTransient(); - } + services.AddSingleton(); + services.AddTransient(); + services.AddTransient(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Handlers/LayerMetadataHandler.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Handlers/LayerMetadataHandler.cs index 1eaec4dfa1f..a90973c331a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Handlers/LayerMetadataHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Handlers/LayerMetadataHandler.cs @@ -5,38 +5,37 @@ using OrchardCore.Documents; using OrchardCore.Layers.Models; -namespace OrchardCore.Layers.Handlers +namespace OrchardCore.Layers.Handlers; + +public class LayerMetadataHandler : ContentHandlerBase { - public class LayerMetadataHandler : ContentHandlerBase + private readonly IVolatileDocumentManager _layerStateManager; + + public LayerMetadataHandler(IVolatileDocumentManager layerStateManager) { - private readonly IVolatileDocumentManager _layerStateManager; + _layerStateManager = layerStateManager; + } - public LayerMetadataHandler(IVolatileDocumentManager layerStateManager) - { - _layerStateManager = layerStateManager; - } + public override Task PublishedAsync(PublishContentContext context) => UpdateAsync(context.ContentItem); - public override Task PublishedAsync(PublishContentContext context) => UpdateAsync(context.ContentItem); + public override Task RemovedAsync(RemoveContentContext context) => UpdateAsync(context.ContentItem); - public override Task RemovedAsync(RemoveContentContext context) => UpdateAsync(context.ContentItem); + public override Task UnpublishedAsync(PublishContentContext context) => UpdateAsync(context.ContentItem); - public override Task UnpublishedAsync(PublishContentContext context) => UpdateAsync(context.ContentItem); + private Task UpdateAsync(ContentItem contentItem) + { + var layerMetadata = contentItem.As(); - private Task UpdateAsync(ContentItem contentItem) + if (layerMetadata == null) { - var layerMetadata = contentItem.As(); - - if (layerMetadata == null) - { - return Task.CompletedTask; - } - - // Checked by the 'LayerFilter'. - return _layerStateManager.UpdateAsync(new LayerState()); + return Task.CompletedTask; } - } - public class LayerState : Document - { + // Checked by the 'LayerFilter'. + return _layerStateManager.UpdateAsync(new LayerState()); } } + +public class LayerState : Document +{ +} diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Indexes/LayerMetadataIndex.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Indexes/LayerMetadataIndex.cs index d43b04a3226..a829a0fc435 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Indexes/LayerMetadataIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Indexes/LayerMetadataIndex.cs @@ -2,32 +2,31 @@ using OrchardCore.Layers.Models; using YesSql.Indexes; -namespace OrchardCore.Layers.Indexes +namespace OrchardCore.Layers.Indexes; + +public class LayerMetadataIndex : MapIndex { - public class LayerMetadataIndex : MapIndex - { - public string Zone { get; set; } - } + public string Zone { get; set; } +} - public class LayerMetadataIndexProvider : IndexProvider +public class LayerMetadataIndexProvider : IndexProvider +{ + public override void Describe(DescribeContext context) { - public override void Describe(DescribeContext context) - { - context.For() - .When(contentItem => contentItem.Has()) - .Map(contentItem => + context.For() + .When(contentItem => contentItem.Has()) + .Map(contentItem => + { + var layerMetadata = contentItem.As(); + if (layerMetadata == null) { - var layerMetadata = contentItem.As(); - if (layerMetadata == null) - { - return null; - } + return null; + } - return new LayerMetadataIndex - { - Zone = layerMetadata.Zone, - }; - }); - } + return new LayerMetadataIndex + { + Zone = layerMetadata.Zone, + }; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Migrations.cs index 14da631119e..47f48475e70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Migrations.cs @@ -5,49 +5,48 @@ using OrchardCore.Rules.Services; using YesSql.Sql; -namespace OrchardCore.Layers +namespace OrchardCore.Layers; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly ILayerService _layerService; + private readonly IConditionIdGenerator _conditionIdGenerator; + + public Migrations( + ILayerService layerService, + IConditionIdGenerator conditionIdGenerator) + { + _layerService = layerService; + _conditionIdGenerator = conditionIdGenerator; + } + + public async Task CreateAsync() { - private readonly ILayerService _layerService; - private readonly IConditionIdGenerator _conditionIdGenerator; - - public Migrations( - ILayerService layerService, - IConditionIdGenerator conditionIdGenerator) - { - _layerService = layerService; - _conditionIdGenerator = conditionIdGenerator; - } - - public async Task CreateAsync() - { - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("Zone", c => c.WithLength(64)) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LayerMetadataIndex_DocumentId", - "DocumentId", - "Zone") - ); - - // Shortcut other migration steps on new content definition schemas. - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_LayerMetadataIndex_DocumentId", - "DocumentId", - "Zone") - ); - - // Migration was cleaned up in version 2.0. - // Jump to step 3 during create. - return 3; - } + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("Zone", c => c.WithLength(64)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LayerMetadataIndex_DocumentId", + "DocumentId", + "Zone") + ); + + // Shortcut other migration steps on new content definition schemas. + return 3; + } + + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_LayerMetadataIndex_DocumentId", + "DocumentId", + "Zone") + ); + + // Migration was cleaned up in version 2.0. + // Jump to step 3 during create. + return 3; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Models/Layer.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Models/Layer.cs index a0f9949f4d3..39bb1833250 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Models/Layer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Models/Layer.cs @@ -1,13 +1,12 @@ using OrchardCore.Rules; -namespace OrchardCore.Layers.Models +namespace OrchardCore.Layers.Models; + +public class Layer { - public class Layer - { - public string Name { get; set; } + public string Name { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public Rule LayerRule { get; set; } - } + public Rule LayerRule { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerMetadata.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerMetadata.cs index 4aa0af4b459..a61d831c0fd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerMetadata.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerMetadata.cs @@ -1,12 +1,11 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Layers.Models +namespace OrchardCore.Layers.Models; + +public class LayerMetadata : ContentPart { - public class LayerMetadata : ContentPart - { - public bool RenderTitle { get; set; } - public double Position { get; set; } - public string Zone { get; set; } - public string Layer { get; set; } - } + public bool RenderTitle { get; set; } + public double Position { get; set; } + public string Zone { get; set; } + public string Layer { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerSettings.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerSettings.cs index 42fd02c2dba..0cb0700dee6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayerSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Layers.Models +namespace OrchardCore.Layers.Models; + +public class LayerSettings { - public class LayerSettings - { - public string[] Zones { get; set; } = []; - } + public string[] Zones { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayersDocument.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayersDocument.cs index 90cc42006fd..4b384f9dfab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayersDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Models/LayersDocument.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Layers.Models +namespace OrchardCore.Layers.Models; + +public class LayersDocument : Document { - public class LayersDocument : Document - { - public List Layers { get; set; } = []; - } + public List Layers { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Recipes/LayerStep.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Recipes/LayerStep.cs index bd84fa077fb..a25dd30c4ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Recipes/LayerStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Recipes/LayerStep.cs @@ -14,139 +14,138 @@ using OrchardCore.Rules; using OrchardCore.Rules.Services; -namespace OrchardCore.Layers.Recipes +namespace OrchardCore.Layers.Recipes; + +/// +/// This recipe step creates or updates a layer. +/// +public sealed class LayerStep : IRecipeStepHandler { - /// - /// This recipe step creates or updates a layer. - /// - public sealed class LayerStep : IRecipeStepHandler + private readonly ILayerService _layerService; + private readonly IConditionIdGenerator _conditionIdGenerator; + private readonly IEnumerable _factories; + private readonly JsonSerializerOptions _serializationOptions; + + internal readonly IStringLocalizer S; + + public LayerStep( + ILayerService layerService, + IConditionIdGenerator conditionIdGenerator, + IEnumerable factories, + IOptions serializationOptions, + IStringLocalizer stringLocalizer) + { + _layerService = layerService; + _conditionIdGenerator = conditionIdGenerator; + _factories = factories; + _serializationOptions = serializationOptions.Value.SerializerOptions; + S = stringLocalizer; + } + + public async Task ExecuteAsync(RecipeExecutionContext context) { - private readonly ILayerService _layerService; - private readonly IConditionIdGenerator _conditionIdGenerator; - private readonly IEnumerable _factories; - private readonly JsonSerializerOptions _serializationOptions; - - internal readonly IStringLocalizer S; - - public LayerStep( - ILayerService layerService, - IConditionIdGenerator conditionIdGenerator, - IEnumerable factories, - IOptions serializationOptions, - IStringLocalizer stringLocalizer) + if (!string.Equals(context.Name, "Layers", StringComparison.OrdinalIgnoreCase)) { - _layerService = layerService; - _conditionIdGenerator = conditionIdGenerator; - _factories = factories; - _serializationOptions = serializationOptions.Value.SerializerOptions; - S = stringLocalizer; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "Layers", StringComparison.OrdinalIgnoreCase)) - { - return; - } + // The recipe step contains polymorphic types which need to be resolved + var model = context.Step.ToObject(_serializationOptions); - // The recipe step contains polymorphic types which need to be resolved - var model = context.Step.ToObject(_serializationOptions); + var allLayers = await _layerService.LoadLayersAsync(); - var allLayers = await _layerService.LoadLayersAsync(); + var unknownTypes = new List(); + var factories = _factories.ToDictionary(x => x.Name); - var unknownTypes = new List(); - var factories = _factories.ToDictionary(x => x.Name); + foreach (var layerStep in model.Layers) + { + var layer = allLayers.Layers.FirstOrDefault(x => string.Equals(x.Name, layerStep.Name, StringComparison.OrdinalIgnoreCase)); - foreach (var layerStep in model.Layers) + if (layer == null) { - var layer = allLayers.Layers.FirstOrDefault(x => string.Equals(x.Name, layerStep.Name, StringComparison.OrdinalIgnoreCase)); + layer = new Layer(); + allLayers.Layers.Add(layer); + } - if (layer == null) - { - layer = new Layer(); - allLayers.Layers.Add(layer); - } + // Backwards compatibility check. + if (layer.LayerRule == null) + { + layer.LayerRule = new Rule(); + _conditionIdGenerator.GenerateUniqueId(layer.LayerRule); + } - // Backwards compatibility check. - if (layer.LayerRule == null) - { - layer.LayerRule = new Rule(); - _conditionIdGenerator.GenerateUniqueId(layer.LayerRule); - } + // Replace any property that is set in the recipe step. + if (!string.IsNullOrEmpty(layerStep.Name)) + { + layer.Name = layerStep.Name; + } + else + { + context.Errors.Add(S["The layer '{0}' is required.", layer.Name]); - // Replace any property that is set in the recipe step. - if (!string.IsNullOrEmpty(layerStep.Name)) - { - layer.Name = layerStep.Name; - } - else - { - context.Errors.Add(S["The layer '{0}' is required.", layer.Name]); + continue; + } - continue; + if (layerStep.LayerRule != null) + { + if (!string.IsNullOrEmpty(layerStep.LayerRule.ConditionId)) + { + layer.LayerRule.ConditionId = layerStep.LayerRule.ConditionId; } - if (layerStep.LayerRule != null) + // The conditions list is cleared, because we cannot logically merge conditions. + layer.LayerRule.Conditions.Clear(); + foreach (var jCondition in layerStep.LayerRule.Conditions) { - if (!string.IsNullOrEmpty(layerStep.LayerRule.ConditionId)) + var name = jCondition["Name"].ToString(); + if (factories.TryGetValue(name, out var factory)) { - layer.LayerRule.ConditionId = layerStep.LayerRule.ConditionId; - } + var factoryCondition = (Condition)jCondition.ToObject(factory.Create().GetType(), _serializationOptions); - // The conditions list is cleared, because we cannot logically merge conditions. - layer.LayerRule.Conditions.Clear(); - foreach (var jCondition in layerStep.LayerRule.Conditions) + layer.LayerRule.Conditions.Add(factoryCondition); + } + else { - var name = jCondition["Name"].ToString(); - if (factories.TryGetValue(name, out var factory)) - { - var factoryCondition = (Condition)jCondition.ToObject(factory.Create().GetType(), _serializationOptions); - - layer.LayerRule.Conditions.Add(factoryCondition); - } - else - { - unknownTypes.Add(name); - } + unknownTypes.Add(name); } } - - if (!string.IsNullOrEmpty(layerStep.Description)) - { - layer.Description = layerStep.Description; - } } - if (unknownTypes.Count != 0) + if (!string.IsNullOrEmpty(layerStep.Description)) { - context.Errors.Add(S["No changes have been made. The following types of conditions cannot be added: {0}. Please ensure that the related features are enabled to add these types of conditions.", string.Join(", ", unknownTypes)]); - - return; + layer.Description = layerStep.Description; } + } + + if (unknownTypes.Count != 0) + { + context.Errors.Add(S["No changes have been made. The following types of conditions cannot be added: {0}. Please ensure that the related features are enabled to add these types of conditions.", string.Join(", ", unknownTypes)]); - await _layerService.UpdateAsync(allLayers); + return; } - } - public class LayersStepModel - { - public LayerStepModel[] Layers { get; set; } + await _layerService.UpdateAsync(allLayers); } +} - public class LayerStepModel - { - public string Name { get; set; } +public class LayersStepModel +{ + public LayerStepModel[] Layers { get; set; } +} - public string Rule { get; set; } - public string Description { get; set; } +public class LayerStepModel +{ + public string Name { get; set; } - public RuleStepModel LayerRule { get; set; } - } + public string Rule { get; set; } + public string Description { get; set; } - public class RuleStepModel - { - public string Name { get; set; } - public string ConditionId { get; set; } - public JsonArray Conditions { get; set; } - } + public RuleStepModel LayerRule { get; set; } +} + +public class RuleStepModel +{ + public string Name { get; set; } + public string ConditionId { get; set; } + public JsonArray Conditions { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Services/DefaultLayersMethodProvider.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Services/DefaultLayersMethodProvider.cs index 5d441541580..9d7c455b07e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Services/DefaultLayersMethodProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Services/DefaultLayersMethodProvider.cs @@ -8,110 +8,109 @@ using Microsoft.Extensions.Options; using OrchardCore.Scripting; -namespace OrchardCore.Layers.Services +namespace OrchardCore.Layers.Services; + +public class DefaultLayersMethodProvider : IGlobalMethodProvider { - public class DefaultLayersMethodProvider : IGlobalMethodProvider - { - private readonly GlobalMethod _isHomepage; - private readonly GlobalMethod _isAnonymous; - private readonly GlobalMethod _isAuthenticated; - private readonly GlobalMethod _isInRole; - private readonly GlobalMethod _url; - private readonly GlobalMethod _culture; + private readonly GlobalMethod _isHomepage; + private readonly GlobalMethod _isAnonymous; + private readonly GlobalMethod _isAuthenticated; + private readonly GlobalMethod _isInRole; + private readonly GlobalMethod _url; + private readonly GlobalMethod _culture; - private readonly GlobalMethod[] _allMethods; + private readonly GlobalMethod[] _allMethods; - public DefaultLayersMethodProvider() + public DefaultLayersMethodProvider() + { + _isHomepage = new GlobalMethod { - _isHomepage = new GlobalMethod + Name = "isHomepage", + Method = serviceProvider => (Func)(() => { - Name = "isHomepage", - Method = serviceProvider => (Func)(() => - { - var httpContext = serviceProvider.GetRequiredService().HttpContext; - var requestPath = httpContext.Request.Path.Value; - return requestPath == "/" || string.IsNullOrEmpty(requestPath); - }), - }; + var httpContext = serviceProvider.GetRequiredService().HttpContext; + var requestPath = httpContext.Request.Path.Value; + return requestPath == "/" || string.IsNullOrEmpty(requestPath); + }), + }; - _isAnonymous = new GlobalMethod + _isAnonymous = new GlobalMethod + { + Name = "isAnonymous", + Method = serviceProvider => (Func)(() => { - Name = "isAnonymous", - Method = serviceProvider => (Func)(() => - { - var httpContext = serviceProvider.GetRequiredService().HttpContext; - return httpContext.User?.Identity.IsAuthenticated != true; - }), - }; + var httpContext = serviceProvider.GetRequiredService().HttpContext; + return httpContext.User?.Identity.IsAuthenticated != true; + }), + }; - _isAuthenticated = new GlobalMethod + _isAuthenticated = new GlobalMethod + { + Name = "isAuthenticated", + Method = serviceProvider => (Func)(() => { - Name = "isAuthenticated", - Method = serviceProvider => (Func)(() => - { - var httpContext = serviceProvider.GetRequiredService().HttpContext; - return httpContext.User?.Identity.IsAuthenticated == true; - }), - }; + var httpContext = serviceProvider.GetRequiredService().HttpContext; + return httpContext.User?.Identity.IsAuthenticated == true; + }), + }; - _isInRole = new GlobalMethod + _isInRole = new GlobalMethod + { + Name = "isInRole", + Method = serviceProvider => (Func)(role => { - Name = "isInRole", - Method = serviceProvider => (Func)(role => - { - var httpContext = serviceProvider.GetRequiredService().HttpContext; - var optionsAccessor = serviceProvider.GetRequiredService>(); - var roleClaimType = optionsAccessor.Value.ClaimsIdentity.RoleClaimType; + var httpContext = serviceProvider.GetRequiredService().HttpContext; + var optionsAccessor = serviceProvider.GetRequiredService>(); + var roleClaimType = optionsAccessor.Value.ClaimsIdentity.RoleClaimType; - // IsInRole() & HasClaim() are case sensitive. - return httpContext.User?.Claims.Any(claim => claim.Type == roleClaimType && claim.Value.Equals(role, StringComparison.OrdinalIgnoreCase)) == true; - }), - }; + // IsInRole() & HasClaim() are case sensitive. + return httpContext.User?.Claims.Any(claim => claim.Type == roleClaimType && claim.Value.Equals(role, StringComparison.OrdinalIgnoreCase)) == true; + }), + }; - _url = new GlobalMethod - { - Name = "url", - Method = serviceProvider => (Func)(url => - { - if (url.StartsWith("~/", StringComparison.Ordinal)) - { - url = url[1..]; - } - - var httpContext = serviceProvider.GetRequiredService().HttpContext; - var requestPath = httpContext.Request.Path.Value; - - // Tenant home page could have an empty string as a request path, where - // the default tenant does not. - if (string.IsNullOrEmpty(requestPath)) - { - requestPath = "/"; - } - - return url.EndsWith('*') - ? requestPath.StartsWith(url.TrimEnd('*'), StringComparison.OrdinalIgnoreCase) - : string.Equals(requestPath, url, StringComparison.OrdinalIgnoreCase); - }), - }; - - _culture = new GlobalMethod + _url = new GlobalMethod + { + Name = "url", + Method = serviceProvider => (Func)(url => { - Name = "culture", - Method = serviceProvider => (Func)(culture => + if (url.StartsWith("~/", StringComparison.Ordinal)) { - var currentCulture = CultureInfo.CurrentCulture; + url = url[1..]; + } - return string.Equals(culture, currentCulture.Name, StringComparison.OrdinalIgnoreCase) || - string.Equals(culture, currentCulture.Parent.Name, StringComparison.OrdinalIgnoreCase); - }), - }; + var httpContext = serviceProvider.GetRequiredService().HttpContext; + var requestPath = httpContext.Request.Path.Value; - _allMethods = [_isAnonymous, _isAuthenticated, _isInRole, _isHomepage, _url, _culture]; - } + // Tenant home page could have an empty string as a request path, where + // the default tenant does not. + if (string.IsNullOrEmpty(requestPath)) + { + requestPath = "/"; + } - public IEnumerable GetMethods() + return url.EndsWith('*') + ? requestPath.StartsWith(url.TrimEnd('*'), StringComparison.OrdinalIgnoreCase) + : string.Equals(requestPath, url, StringComparison.OrdinalIgnoreCase); + }), + }; + + _culture = new GlobalMethod { - return _allMethods; - } + Name = "culture", + Method = serviceProvider => (Func)(culture => + { + var currentCulture = CultureInfo.CurrentCulture; + + return string.Equals(culture, currentCulture.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals(culture, currentCulture.Parent.Name, StringComparison.OrdinalIgnoreCase); + }), + }; + + _allMethods = [_isAnonymous, _isAuthenticated, _isInRole, _isHomepage, _url, _culture]; + } + + public IEnumerable GetMethods() + { + return _allMethods; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Services/ILayerService.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Services/ILayerService.cs index 9b9d13ba692..10b67407a34 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Services/ILayerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Services/ILayerService.cs @@ -6,26 +6,25 @@ using OrchardCore.ContentManagement.Records; using OrchardCore.Layers.Models; -namespace OrchardCore.Layers.Services +namespace OrchardCore.Layers.Services; + +public interface ILayerService { - public interface ILayerService - { - /// - /// Loads the layers document from the store for updating and that should not be cached. - /// - Task LoadLayersAsync(); + /// + /// Loads the layers document from the store for updating and that should not be cached. + /// + Task LoadLayersAsync(); - /// - /// Gets the layers document from the cache for sharing and that should not be updated. - /// - Task GetLayersAsync(); + /// + /// Gets the layers document from the cache for sharing and that should not be updated. + /// + Task GetLayersAsync(); - Task> GetLayerWidgetsAsync(Expression> predicate); - Task> GetLayerWidgetsMetadataAsync(Expression> predicate); + Task> GetLayerWidgetsAsync(Expression> predicate); + Task> GetLayerWidgetsMetadataAsync(Expression> predicate); - /// - /// Updates the store with the provided layers document and then updates the cache. - /// - Task UpdateAsync(LayersDocument layers); - } + /// + /// Updates the store with the provided layers document and then updates the cache. + /// + Task UpdateAsync(LayersDocument layers); } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerFilter.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerFilter.cs index 4a510deec1b..1f31ea11be0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerFilter.cs @@ -17,135 +17,134 @@ using OrchardCore.Mvc.Utilities; using OrchardCore.Rules.Services; -namespace OrchardCore.Layers.Services +namespace OrchardCore.Layers.Services; + +public sealed class LayerFilter : IAsyncResultFilter { - public sealed class LayerFilter : IAsyncResultFilter + private const string WidgetsKey = "OrchardCore.Layers.LayerFilter:AllWidgets"; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ILayoutAccessor _layoutAccessor; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IUpdateModelAccessor _modelUpdaterAccessor; + private readonly IRuleService _ruleService; + private readonly IMemoryCache _memoryCache; + private readonly IThemeManager _themeManager; + private readonly IAdminThemeService _adminThemeService; + private readonly ILayerService _layerService; + private readonly IVolatileDocumentManager _layerStateManager; + + public LayerFilter( + IContentDefinitionManager contentDefinitionManager, + ILayerService layerService, + ILayoutAccessor layoutAccessor, + IContentItemDisplayManager contentItemDisplayManager, + IUpdateModelAccessor modelUpdaterAccessor, + IRuleService ruleService, + IMemoryCache memoryCache, + IThemeManager themeManager, + IAdminThemeService adminThemeService, + IVolatileDocumentManager layerStateManager) { - private const string WidgetsKey = "OrchardCore.Layers.LayerFilter:AllWidgets"; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ILayoutAccessor _layoutAccessor; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IUpdateModelAccessor _modelUpdaterAccessor; - private readonly IRuleService _ruleService; - private readonly IMemoryCache _memoryCache; - private readonly IThemeManager _themeManager; - private readonly IAdminThemeService _adminThemeService; - private readonly ILayerService _layerService; - private readonly IVolatileDocumentManager _layerStateManager; - - public LayerFilter( - IContentDefinitionManager contentDefinitionManager, - ILayerService layerService, - ILayoutAccessor layoutAccessor, - IContentItemDisplayManager contentItemDisplayManager, - IUpdateModelAccessor modelUpdaterAccessor, - IRuleService ruleService, - IMemoryCache memoryCache, - IThemeManager themeManager, - IAdminThemeService adminThemeService, - IVolatileDocumentManager layerStateManager) - { - _contentDefinitionManager = contentDefinitionManager; - _layerService = layerService; - _layoutAccessor = layoutAccessor; - _contentItemDisplayManager = contentItemDisplayManager; - _modelUpdaterAccessor = modelUpdaterAccessor; - _ruleService = ruleService; - _memoryCache = memoryCache; - _themeManager = themeManager; - _adminThemeService = adminThemeService; - _layerStateManager = layerStateManager; - } + _contentDefinitionManager = contentDefinitionManager; + _layerService = layerService; + _layoutAccessor = layoutAccessor; + _contentItemDisplayManager = contentItemDisplayManager; + _modelUpdaterAccessor = modelUpdaterAccessor; + _ruleService = ruleService; + _memoryCache = memoryCache; + _themeManager = themeManager; + _adminThemeService = adminThemeService; + _layerStateManager = layerStateManager; + } - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + // Should only run on the front-end for a full view + if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) { - // Should only run on the front-end for a full view - if (context.IsViewOrPageResult() && !AdminAttribute.IsApplied(context.HttpContext)) + // Even if the Admin attribute is not applied we might be using the admin theme, for instance in Login views. + // In this case don't render Layers. + var selectedTheme = (await _themeManager.GetThemeAsync())?.Id; + var adminTheme = await _adminThemeService.GetAdminThemeNameAsync(); + if (selectedTheme == adminTheme) { - // Even if the Admin attribute is not applied we might be using the admin theme, for instance in Login views. - // In this case don't render Layers. - var selectedTheme = (await _themeManager.GetThemeAsync())?.Id; - var adminTheme = await _adminThemeService.GetAdminThemeNameAsync(); - if (selectedTheme == adminTheme) - { - await next.Invoke(); - return; - } + await next.Invoke(); + return; + } - var layerState = await _layerStateManager.GetOrCreateImmutableAsync(); + var layerState = await _layerStateManager.GetOrCreateImmutableAsync(); - if (!_memoryCache.TryGetValue(WidgetsKey, out var cacheEntry) || cacheEntry.Identifier != layerState.Identifier) + if (!_memoryCache.TryGetValue(WidgetsKey, out var cacheEntry) || cacheEntry.Identifier != layerState.Identifier) + { + cacheEntry = new CacheEntry() { - cacheEntry = new CacheEntry() - { - Identifier = layerState.Identifier, - Widgets = await _layerService.GetLayerWidgetsMetadataAsync(x => x.Published) - }; + Identifier = layerState.Identifier, + Widgets = await _layerService.GetLayerWidgetsMetadataAsync(x => x.Published) + }; - _memoryCache.Set(WidgetsKey, cacheEntry); - } + _memoryCache.Set(WidgetsKey, cacheEntry); + } - var widgets = cacheEntry.Widgets; + var widgets = cacheEntry.Widgets; - var layers = (await _layerService.GetLayersAsync()).Layers.ToDictionary(x => x.Name); + var layers = (await _layerService.GetLayersAsync()).Layers.ToDictionary(x => x.Name); - var layout = await _layoutAccessor.GetLayoutAsync(); - var updater = _modelUpdaterAccessor.ModelUpdater; + var layout = await _layoutAccessor.GetLayoutAsync(); + var updater = _modelUpdaterAccessor.ModelUpdater; - var layersCache = new Dictionary(); - var contentDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + var layersCache = new Dictionary(); + var contentDefinitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); - foreach (var widget in widgets) - { - var layer = widget.Layer != null && layers.TryGetValue(widget.Layer, out var widgetLayer) ? widgetLayer : null; + foreach (var widget in widgets) + { + var layer = widget.Layer != null && layers.TryGetValue(widget.Layer, out var widgetLayer) ? widgetLayer : null; - if (layer == null) - { - continue; - } + if (layer == null) + { + continue; + } - bool display; - if (!layersCache.TryGetValue(layer.Name, out display)) - { - display = await _ruleService.EvaluateAsync(layer.LayerRule); + bool display; + if (!layersCache.TryGetValue(layer.Name, out display)) + { + display = await _ruleService.EvaluateAsync(layer.LayerRule); - layersCache[layer.Name] = display; - } + layersCache[layer.Name] = display; + } - if (!display) - { - continue; - } + if (!display) + { + continue; + } - if (contentDefinitions.Any(c => c.Name == widget.ContentItem.ContentType)) - { - var widgetContent = await _contentItemDisplayManager.BuildDisplayAsync(widget.ContentItem, updater); + if (contentDefinitions.Any(c => c.Name == widget.ContentItem.ContentType)) + { + var widgetContent = await _contentItemDisplayManager.BuildDisplayAsync(widget.ContentItem, updater); - widgetContent.Classes.Add("widget"); - widgetContent.Classes.Add("widget-" + widget.ContentItem.ContentType.HtmlClassify()); + widgetContent.Classes.Add("widget"); + widgetContent.Classes.Add("widget-" + widget.ContentItem.ContentType.HtmlClassify()); - var wrapper = new WidgetWrapper - { - Widget = widget.ContentItem, - Content = widgetContent - }; + var wrapper = new WidgetWrapper + { + Widget = widget.ContentItem, + Content = widgetContent + }; - wrapper.Metadata.Alternates.Add("Widget_Wrapper__" + widget.ContentItem.ContentType); - wrapper.Metadata.Alternates.Add("Widget_Wrapper__Zone__" + widget.Zone); + wrapper.Metadata.Alternates.Add("Widget_Wrapper__" + widget.ContentItem.ContentType); + wrapper.Metadata.Alternates.Add("Widget_Wrapper__Zone__" + widget.Zone); - var contentZone = layout.Zones[widget.Zone]; + var contentZone = layout.Zones[widget.Zone]; - await contentZone.AddAsync(wrapper, ""); - } + await contentZone.AddAsync(wrapper, ""); } } - - await next.Invoke(); } - internal sealed class CacheEntry : Document - { - public IEnumerable Widgets { get; set; } - } + await next.Invoke(); + } + + internal sealed class CacheEntry : Document + { + public IEnumerable Widgets { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerService.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerService.cs index ee4b29c714e..7bb67d60188 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Services/LayerService.cs @@ -10,58 +10,57 @@ using OrchardCore.Layers.Models; using YesSql; -namespace OrchardCore.Layers.Services +namespace OrchardCore.Layers.Services; + +public class LayerService : ILayerService { - public class LayerService : ILayerService - { - private readonly ISession _session; - private readonly IDocumentManager _documentManager; + private readonly ISession _session; + private readonly IDocumentManager _documentManager; - public LayerService(ISession session, IDocumentManager documentManager) - { - _session = session; - _documentManager = documentManager; - } + public LayerService(ISession session, IDocumentManager documentManager) + { + _session = session; + _documentManager = documentManager; + } - /// - /// Loads the layers document from the store for updating and that should not be cached. - /// - public Task LoadLayersAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Loads the layers document from the store for updating and that should not be cached. + /// + public Task LoadLayersAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - /// Gets the layers document from the cache for sharing and that should not be updated. - /// - public Task GetLayersAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + /// Gets the layers document from the cache for sharing and that should not be updated. + /// + public Task GetLayersAsync() => _documentManager.GetOrCreateImmutableAsync(); - public Task> GetLayerWidgetsAsync( - Expression> predicate) - { - return _session - .Query() - .With(predicate) - .ListAsync(); - } + public Task> GetLayerWidgetsAsync( + Expression> predicate) + { + return _session + .Query() + .With(predicate) + .ListAsync(); + } - public async Task> GetLayerWidgetsMetadataAsync( - Expression> predicate) - { - var allWidgets = await GetLayerWidgetsAsync(predicate); + public async Task> GetLayerWidgetsMetadataAsync( + Expression> predicate) + { + var allWidgets = await GetLayerWidgetsAsync(predicate); - return allWidgets - .Select(x => x.As()) - .Where(x => x != null) - .OrderBy(x => x.Position) - .ToList(); - } + return allWidgets + .Select(x => x.As()) + .Where(x => x != null) + .OrderBy(x => x.Position) + .ToList(); + } - /// - /// Updates the store with the provided layers document and then updates the cache. - /// - public async Task UpdateAsync(LayersDocument layers) - { - var existing = await LoadLayersAsync(); - existing.Layers = layers.Layers; - await _documentManager.UpdateAsync(layers); - } + /// + /// Updates the store with the provided layers document and then updates the cache. + /// + public async Task UpdateAsync(LayersDocument layers) + { + var existing = await LoadLayersAsync(); + existing.Layers = layers.Layers; + await _documentManager.UpdateAsync(layers); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Layers/Startup.cs index 72b289fdcfd..018ddce7002 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/Startup.cs @@ -23,34 +23,33 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Layers +namespace OrchardCore.Layers; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }); + o.MemberAccessStrategy.Register(); + }); - services.Configure((options) => - { - options.Filters.Add(); - }); + services.Configure((options) => + { + options.Filters.Add(); + }); - services.AddScoped, LayerSiteSettingsDisplayDriver>(); - services.AddContentPart(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddIndexProvider(); - services.AddDataMigration(); - services.AddScoped(); - services.AddRecipeExecutionStep(); - services.AddDeployment(); - services.AddSingleton(); - } + services.AddScoped, LayerSiteSettingsDisplayDriver>(); + services.AddContentPart(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddIndexProvider(); + services.AddDataMigration(); + services.AddScoped(); + services.AddRecipeExecutionStep(); + services.AddDeployment(); + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerEditViewModel.cs index 9f3eeea6795..64962e81e13 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerEditViewModel.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayerEditViewModel { - public class LayerEditViewModel - { - public string Name { get; set; } - public string Description { get; set; } - public dynamic LayerRule { get; set; } - public Dictionary Thumbnails { get; set; } - } + public string Name { get; set; } + public string Description { get; set; } + public dynamic LayerRule { get; set; } + public Dictionary Thumbnails { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerMetadataEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerMetadataEditViewModel.cs index 3bf08860c04..53f8c1c3fac 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerMetadataEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerMetadataEditViewModel.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using OrchardCore.Layers.Models; -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayerMetadataEditViewModel { - public class LayerMetadataEditViewModel - { - public string Title { get; set; } - public LayerMetadata LayerMetadata { get; set; } - public List Layers { get; set; } - } + public string Title { get; set; } + public LayerMetadata LayerMetadata { get; set; } + public List Layers { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleCreateViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleCreateViewModel.cs index fb42ff7649a..4801e0b7805 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleCreateViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleCreateViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayerRuleCreateViewModel { - public class LayerRuleCreateViewModel - { - public string Name { get; set; } - public string ConditionGroupId { get; set; } - public string ConditionType { get; set; } - public dynamic Editor { get; set; } - } + public string Name { get; set; } + public string ConditionGroupId { get; set; } + public string ConditionType { get; set; } + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleEditViewModel.cs index d97e7a8def8..51f2bb47412 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerRuleEditViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayerRuleEditViewModel { - public class LayerRuleEditViewModel - { - public string Name { get; set; } - public string ConditionGroupId { get; set; } - public string ConditionId { get; set; } - public string ConditionType { get; set; } - public dynamic Editor { get; set; } - } + public string Name { get; set; } + public string ConditionGroupId { get; set; } + public string ConditionId { get; set; } + public string ConditionType { get; set; } + public dynamic Editor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerSettingsViewModel.cs index 369ced6d37c..bd732a690ab 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayerSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayerSettingsViewModel { - public class LayerSettingsViewModel - { - public string Zones { get; set; } - } + public string Zones { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayersIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayersIndexViewModel.cs index d651a4b48ef..c81d45c0322 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayersIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/LayersIndexViewModel.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Layers.Models; -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class LayersIndexViewModel { - public class LayersIndexViewModel - { - [BindNever] - public string[] Zones { get; set; } + [BindNever] + public string[] Zones { get; set; } - [BindNever] - public Dictionary> Widgets { get; set; } = []; + [BindNever] + public Dictionary> Widgets { get; set; } = []; - [BindNever] - public List Layers { get; set; } - } + [BindNever] + public List Layers { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/WidgetWrapper.cs b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/WidgetWrapper.cs index a1111edfe90..5530defaf20 100644 --- a/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/WidgetWrapper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Layers/ViewModels/WidgetWrapper.cs @@ -2,15 +2,14 @@ using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Layers.ViewModels +namespace OrchardCore.Layers.ViewModels; + +public class WidgetWrapper : ShapeViewModel { - public class WidgetWrapper : ShapeViewModel + public WidgetWrapper() : base("Widget_Wrapper") { - public WidgetWrapper() : base("Widget_Wrapper") - { - } - - public ContentItem Widget { get; set; } - public IShape Content { get; set; } } + + public ContentItem Widget { get; set; } + public IShape Content { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Drivers/LiquidPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Drivers/LiquidPartDisplayDriver.cs index 9c26f574e98..1e9b852ed8f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Drivers/LiquidPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Drivers/LiquidPartDisplayDriver.cs @@ -7,60 +7,59 @@ using OrchardCore.Liquid.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Liquid.Drivers -{ - public sealed class LiquidPartDisplayDriver : ContentPartDisplayDriver - { - private readonly ILiquidTemplateManager _liquidTemplateManager; +namespace OrchardCore.Liquid.Drivers; - internal readonly IStringLocalizer S; +public sealed class LiquidPartDisplayDriver : ContentPartDisplayDriver +{ + private readonly ILiquidTemplateManager _liquidTemplateManager; - public LiquidPartDisplayDriver( - ILiquidTemplateManager liquidTemplateManager, - IStringLocalizer localizer) - { - _liquidTemplateManager = liquidTemplateManager; - S = localizer; - } + internal readonly IStringLocalizer S; - public override Task DisplayAsync(LiquidPart liquidPart, BuildPartDisplayContext context) - { - return CombineAsync( - Initialize("LiquidPart", m => BuildViewModel(m, liquidPart)) - .Location("Detail", "Content"), - Initialize("LiquidPart_Summary", m => BuildViewModel(m, liquidPart)) - .Location("Summary", "Content") - ); - } + public LiquidPartDisplayDriver( + ILiquidTemplateManager liquidTemplateManager, + IStringLocalizer localizer) + { + _liquidTemplateManager = liquidTemplateManager; + S = localizer; + } - public override IDisplayResult Edit(LiquidPart liquidPart, BuildPartEditorContext context) - { - return Initialize("LiquidPart_Edit", m => BuildViewModel(m, liquidPart)); - } + public override Task DisplayAsync(LiquidPart liquidPart, BuildPartDisplayContext context) + { + return CombineAsync( + Initialize("LiquidPart", m => BuildViewModel(m, liquidPart)) + .Location("Detail", "Content"), + Initialize("LiquidPart_Summary", m => BuildViewModel(m, liquidPart)) + .Location("Summary", "Content") + ); + } - public override async Task UpdateAsync(LiquidPart model, UpdatePartEditorContext context) - { - var viewModel = new LiquidPartViewModel(); + public override IDisplayResult Edit(LiquidPart liquidPart, BuildPartEditorContext context) + { + return Initialize("LiquidPart_Edit", m => BuildViewModel(m, liquidPart)); + } - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Liquid); + public override async Task UpdateAsync(LiquidPart model, UpdatePartEditorContext context) + { + var viewModel = new LiquidPartViewModel(); - if (!string.IsNullOrEmpty(viewModel.Liquid) && !_liquidTemplateManager.Validate(viewModel.Liquid, out var errors)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Liquid), S["The Liquid Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } - else - { - model.Liquid = viewModel.Liquid; - } + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, t => t.Liquid); - return Edit(model, context); + if (!string.IsNullOrEmpty(viewModel.Liquid) && !_liquidTemplateManager.Validate(viewModel.Liquid, out var errors)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Liquid), S["The Liquid Body doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); } - - private static void BuildViewModel(LiquidPartViewModel model, LiquidPart liquidPart) + else { - model.Liquid = liquidPart.Liquid; - model.LiquidPart = liquidPart; - model.ContentItem = liquidPart.ContentItem; + model.Liquid = viewModel.Liquid; } + + return Edit(model, context); + } + + private static void BuildViewModel(LiquidPartViewModel model, LiquidPart liquidPart) + { + model.Liquid = liquidPart.Liquid; + model.LiquidPart = liquidPart; + model.ContentItem = liquidPart.ContentItem; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/AbsoluteUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/AbsoluteUrlFilter.cs index 44d525cdaec..a8cb7f96542 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/AbsoluteUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/AbsoluteUrlFilter.cs @@ -4,29 +4,28 @@ using Microsoft.AspNetCore.Mvc.Routing; using OrchardCore.Mvc.Core.Utilities; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class AbsoluteUrlFilter : ILiquidFilter { - public class AbsoluteUrlFilter : ILiquidFilter + private readonly IUrlHelperFactory _urlHelperFactory; + + public AbsoluteUrlFilter(IUrlHelperFactory urlHelperFactory) + { + _urlHelperFactory = urlHelperFactory; + } + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) { - private readonly IUrlHelperFactory _urlHelperFactory; + var relativePath = input.ToStringValue(); - public AbsoluteUrlFilter(IUrlHelperFactory urlHelperFactory) + if (string.IsNullOrWhiteSpace(relativePath)) { - _urlHelperFactory = urlHelperFactory; + return new ValueTask(input); } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) - { - var relativePath = input.ToStringValue(); - - if (string.IsNullOrWhiteSpace(relativePath)) - { - return new ValueTask(input); - } - var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); + var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); - var result = new StringValue(urlHelper.ToAbsoluteUrl(relativePath)); - return new ValueTask(result); - } + var result = new StringValue(urlHelper.ToAbsoluteUrl(relativePath)); + return new ValueTask(result); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ContentUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ContentUrlFilter.cs index f73f4a4f11a..d37cbc6d61a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ContentUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ContentUrlFilter.cs @@ -3,22 +3,21 @@ using Fluid.Values; using Microsoft.AspNetCore.Mvc.Routing; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class ContentUrlFilter : ILiquidFilter { - public class ContentUrlFilter : ILiquidFilter - { - private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IUrlHelperFactory _urlHelperFactory; - public ContentUrlFilter(IUrlHelperFactory urlHelperFactory) - { - _urlHelperFactory = urlHelperFactory; - } + public ContentUrlFilter(IUrlHelperFactory urlHelperFactory) + { + _urlHelperFactory = urlHelperFactory; + } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) - { - var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + { + var urlHelper = _urlHelperFactory.GetUrlHelper(context.ViewContext); - return new ValueTask(new StringValue((urlHelper).Content(input.ToStringValue()))); - } + return new ValueTask(new StringValue((urlHelper).Content(input.ToStringValue()))); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonFilter.cs index acff2ddc9d5..d4c607fc329 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonFilter.cs @@ -5,49 +5,48 @@ using Fluid; using Fluid.Values; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public static class JsonFilter { - public static class JsonFilter + public static ValueTask Json(FluidValue input, FilterArguments arguments, TemplateContext context) { - public static ValueTask Json(FluidValue input, FilterArguments arguments, TemplateContext context) + var formatting = JOptions.Default; + if (arguments.At(0).ToBooleanValue()) { - var formatting = JOptions.Default; - if (arguments.At(0).ToBooleanValue()) - { - formatting = JOptions.Indented; - } - - switch (input.Type) - { - case FluidValues.Array: - return new ValueTask(new StringValue(JConvert.SerializeObject(input.Enumerate(context).Select(o => o.ToObjectValue()), formatting))); + formatting = JOptions.Indented; + } - case FluidValues.Boolean: - return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToBooleanValue(), formatting))); + switch (input.Type) + { + case FluidValues.Array: + return new ValueTask(new StringValue(JConvert.SerializeObject(input.Enumerate(context).Select(o => o.ToObjectValue()), formatting))); - case FluidValues.Nil: - return new ValueTask(StringValue.Create("null")); + case FluidValues.Boolean: + return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToBooleanValue(), formatting))); - case FluidValues.Number: - return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToNumberValue(), formatting))); + case FluidValues.Nil: + return new ValueTask(StringValue.Create("null")); - case FluidValues.DateTime: - case FluidValues.Dictionary: - case FluidValues.Object: - return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToObjectValue(), formatting))); + case FluidValues.Number: + return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToNumberValue(), formatting))); - case FluidValues.String: - var stringValue = input.ToStringValue(); + case FluidValues.DateTime: + case FluidValues.Dictionary: + case FluidValues.Object: + return new ValueTask(new StringValue(JConvert.SerializeObject(input.ToObjectValue(), formatting))); - if (string.IsNullOrWhiteSpace(stringValue)) - { - return new ValueTask(input); - } + case FluidValues.String: + var stringValue = input.ToStringValue(); - return new ValueTask(new StringValue(JConvert.SerializeObject(stringValue, formatting))); - } + if (string.IsNullOrWhiteSpace(stringValue)) + { + return new ValueTask(input); + } - throw new NotSupportedException("Unrecognized FluidValue"); + return new ValueTask(new StringValue(JConvert.SerializeObject(stringValue, formatting))); } + + throw new NotSupportedException("Unrecognized FluidValue"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonParseFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonParseFilter.cs index 39d24d0409c..5205d6b5ae1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonParseFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/JsonParseFilter.cs @@ -4,19 +4,18 @@ using Fluid; using Fluid.Values; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public static class JsonParseFilter { - public static class JsonParseFilter + public static ValueTask JsonParse(FluidValue input, FilterArguments _, TemplateContext context) { - public static ValueTask JsonParse(FluidValue input, FilterArguments _, TemplateContext context) + var parsedValue = JNode.Parse(input.ToStringValue()); + if (parsedValue.GetValueKind() == JsonValueKind.Array) { - var parsedValue = JNode.Parse(input.ToStringValue()); - if (parsedValue.GetValueKind() == JsonValueKind.Array) - { - return new ValueTask(FluidValue.Create(parsedValue, context.Options)); - } - - return new ValueTask(new ObjectValue(parsedValue)); + return new ValueTask(FluidValue.Create(parsedValue, context.Options)); } + + return new ValueTask(new ObjectValue(parsedValue)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LiquidFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LiquidFilter.cs index 4eac1e7b12b..62c2b30931e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LiquidFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LiquidFilter.cs @@ -3,23 +3,22 @@ using Fluid; using Fluid.Values; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class LiquidFilter : ILiquidFilter { - public class LiquidFilter : ILiquidFilter - { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - public LiquidFilter(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) - { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - } - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) - { - var content = await _liquidTemplateManager.RenderStringAsync(input.ToStringValue(), _htmlEncoder, arguments.At(0)); + public LiquidFilter(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + var content = await _liquidTemplateManager.RenderStringAsync(input.ToStringValue(), _htmlEncoder, arguments.At(0)); - return new StringValue(content, false); - } + return new StringValue(content, false); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LocalTimeZoneFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LocalTimeZoneFilter.cs index 57ed849a2f5..bdd4de7efa2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LocalTimeZoneFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/LocalTimeZoneFilter.cs @@ -5,53 +5,52 @@ using Fluid.Values; using OrchardCore.Modules; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class LocalTimeZoneFilter : ILiquidFilter { - public class LocalTimeZoneFilter : ILiquidFilter + private readonly ILocalClock _localClock; + + public LocalTimeZoneFilter(ILocalClock localClock) { - private readonly ILocalClock _localClock; + _localClock = localClock; + } - public LocalTimeZoneFilter(ILocalClock localClock) - { - _localClock = localClock; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + DateTimeOffset value; - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + if (input.Type == FluidValues.String) { - DateTimeOffset value; + var stringValue = input.ToStringValue(); - if (input.Type == FluidValues.String) + if (stringValue == "now" || stringValue == "today") { - var stringValue = input.ToStringValue(); - - if (stringValue == "now" || stringValue == "today") - { - value = await _localClock.LocalNowAsync; - } - else - { - if (!DateTimeOffset.TryParse(stringValue, ctx.Options.CultureInfo, DateTimeStyles.AssumeUniversal, out value)) - { - return NilValue.Instance; - } - } + value = await _localClock.LocalNowAsync; } else { - switch (input.ToObjectValue()) + if (!DateTimeOffset.TryParse(stringValue, ctx.Options.CultureInfo, DateTimeStyles.AssumeUniversal, out value)) { - case DateTime dateTime: - value = dateTime; - break; - case DateTimeOffset dateTimeOffset: - value = dateTimeOffset; - break; - default: - return NilValue.Instance; + return NilValue.Instance; } } - - return new ObjectValue(await _localClock.ConvertToLocalAsync(value)); } + else + { + switch (input.ToObjectValue()) + { + case DateTime dateTime: + value = dateTime; + break; + case DateTimeOffset dateTimeOffset: + value = dateTimeOffset; + break; + default: + return NilValue.Instance; + } + } + + return new ObjectValue(await _localClock.ConvertToLocalAsync(value)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ShortCodeFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ShortCodeFilter.cs index f8d365e6a4c..43b6494bb36 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ShortCodeFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/ShortCodeFilter.cs @@ -5,33 +5,32 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class ShortcodeFilter : ILiquidFilter { - public class ShortcodeFilter : ILiquidFilter + private readonly IShortcodeService _shortcodeService; + + public ShortcodeFilter(IShortcodeService shortcodeService) + { + _shortcodeService = shortcodeService; + } + + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) { - private readonly IShortcodeService _shortcodeService; + var shortcodeContext = new Context(); - public ShortcodeFilter(IShortcodeService shortcodeService) + // Retrieve the 'ContentItem' from the ambient liquid scope. + var model = context.GetValue("Model").ToObjectValue(); + if (model is Shape shape && shape.Properties.TryGetValue("ContentItem", out var contentItem)) { - _shortcodeService = shortcodeService; + shortcodeContext["ContentItem"] = contentItem; } - - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + else { - var shortcodeContext = new Context(); - - // Retrieve the 'ContentItem' from the ambient liquid scope. - var model = context.GetValue("Model").ToObjectValue(); - if (model is Shape shape && shape.Properties.TryGetValue("ContentItem", out var contentItem)) - { - shortcodeContext["ContentItem"] = contentItem; - } - else - { - shortcodeContext["ContentItem"] = null; - } - - return new StringValue(await _shortcodeService.ProcessAsync(input.ToStringValue(), shortcodeContext)); + shortcodeContext["ContentItem"] = null; } + + return new StringValue(await _shortcodeService.ProcessAsync(input.ToStringValue(), shortcodeContext)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/SlugifyFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/SlugifyFilter.cs index 006fd72a293..a8f9c5ab801 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/SlugifyFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/SlugifyFilter.cs @@ -3,21 +3,20 @@ using Fluid.Values; using OrchardCore.Modules.Services; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class SlugifyFilter : ILiquidFilter { - public class SlugifyFilter : ILiquidFilter - { - private readonly ISlugService _slugService; + private readonly ISlugService _slugService; - public SlugifyFilter(ISlugService slugService) - { - _slugService = slugService; - } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) - { - var text = input.ToStringValue(); + public SlugifyFilter(ISlugService slugService) + { + _slugService = slugService; + } + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + var text = input.ToStringValue(); - return new StringValue(_slugService.Slugify(text)); - } + return new StringValue(_slugService.Slugify(text)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/UtcTimeZoneFilter.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/UtcTimeZoneFilter.cs index 4cbe18d59c7..ea8a5cddfcd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/UtcTimeZoneFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Filters/UtcTimeZoneFilter.cs @@ -5,53 +5,52 @@ using Fluid.Values; using OrchardCore.Modules; -namespace OrchardCore.Liquid.Filters +namespace OrchardCore.Liquid.Filters; + +public class UtcTimeZoneFilter : ILiquidFilter { - public class UtcTimeZoneFilter : ILiquidFilter + private readonly ILocalClock _localClock; + + public UtcTimeZoneFilter(ILocalClock localClock) { - private readonly ILocalClock _localClock; + _localClock = localClock; + } - public UtcTimeZoneFilter(ILocalClock localClock) - { - _localClock = localClock; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + DateTimeOffset value; - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + if (input.Type == FluidValues.String) { - DateTimeOffset value; + var stringValue = input.ToStringValue(); - if (input.Type == FluidValues.String) + if (stringValue == "now" || stringValue == "today") { - var stringValue = input.ToStringValue(); - - if (stringValue == "now" || stringValue == "today") - { - value = await _localClock.LocalNowAsync; - } - else - { - if (!DateTimeOffset.TryParse(stringValue, ctx.Options.CultureInfo, DateTimeStyles.AssumeUniversal, out value)) - { - return NilValue.Instance; - } - } + value = await _localClock.LocalNowAsync; } else { - switch (input.ToObjectValue()) + if (!DateTimeOffset.TryParse(stringValue, ctx.Options.CultureInfo, DateTimeStyles.AssumeUniversal, out value)) { - case DateTime dateTime: - value = dateTime; - break; - case DateTimeOffset dateTimeOffset: - value = dateTimeOffset; - break; - default: - return NilValue.Instance; + return NilValue.Instance; } } - - return new ObjectValue(await _localClock.ConvertToUtcAsync(value.DateTime)); } + else + { + switch (input.ToObjectValue()) + { + case DateTime dateTime: + value = dateTime; + break; + case DateTimeOffset dateTimeOffset: + value = dateTimeOffset; + break; + default: + return NilValue.Instance; + } + } + + return new ObjectValue(await _localClock.ConvertToUtcAsync(value.DateTime)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Handlers/LiquidPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Handlers/LiquidPartHandler.cs index f75a97cfa85..e160c94373d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Handlers/LiquidPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Handlers/LiquidPartHandler.cs @@ -8,41 +8,40 @@ using OrchardCore.Liquid.Models; using OrchardCore.Liquid.ViewModels; -namespace OrchardCore.Liquid.Handlers +namespace OrchardCore.Liquid.Handlers; + +public class LiquidPartHandler : ContentPartHandler { - public class LiquidPartHandler : ContentPartHandler - { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - public LiquidPartHandler(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) - { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - } + public LiquidPartHandler(ILiquidTemplateManager liquidTemplateManager, HtmlEncoder htmlEncoder) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, LiquidPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, LiquidPart part) + { + return context.ForAsync(async bodyAspect => { - return context.ForAsync(async bodyAspect => + try { - try + var model = new LiquidPartViewModel() { - var model = new LiquidPartViewModel() - { - LiquidPart = part, - ContentItem = part.ContentItem, - }; + LiquidPart = part, + ContentItem = part.ContentItem, + }; - var result = await _liquidTemplateManager.RenderHtmlContentAsync(part.Liquid, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + var result = await _liquidTemplateManager.RenderHtmlContentAsync(part.Liquid, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - bodyAspect.Body = result; - } - catch - { - bodyAspect.Body = HtmlString.Empty; - } - }); - } + bodyAspect.Body = result; + } + catch + { + bodyAspect.Body = HtmlString.Empty; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Indexing/LiquidPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Indexing/LiquidPartIndexHandler.cs index 9a3fbb394a6..609dc9c6247 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Indexing/LiquidPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Indexing/LiquidPartIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.Indexing; using OrchardCore.Liquid.Models; -namespace OrchardCore.Liquid.Indexing +namespace OrchardCore.Liquid.Indexing; + +public class LiquidPartIndexHandler : ContentPartIndexHandler { - public class LiquidPartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(LiquidPart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(LiquidPart part, BuildPartIndexContext context) - { - var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, part.Liquid, options); - } + var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, part.Liquid, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Migrations.cs index 575b9526546..2888a978099 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Migrations.cs @@ -3,24 +3,23 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("LiquidPart", builder => builder - .Attachable() - .WithDescription("Provides a Liquid formatted body for your content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("LiquidPart", builder => builder + .Attachable() + .WithDescription("Provides a Liquid formatted body for your content item.")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Models/LiquidPart.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Models/LiquidPart.cs index 1a1075d507a..b8dbbcfada5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Models/LiquidPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Models/LiquidPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Liquid.Models +namespace OrchardCore.Liquid.Models; + +public class LiquidPart : ContentPart { - public class LiquidPart : ContentPart - { - public string Liquid { get; set; } - } + public string Liquid { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/ResourceManagementOptionsConfiguration.cs index d19426e1f49..f09e6fffad6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/ResourceManagementOptionsConfiguration.cs @@ -1,31 +1,30 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions - { - private static readonly ResourceManifest _manifest; + private static readonly ResourceManifest _manifest; - static ResourceManagementOptionsConfiguration() - { - _manifest = new ResourceManifest(); + static ResourceManagementOptionsConfiguration() + { + _manifest = new ResourceManifest(); - _manifest - .DefineScript("monaco-liquid-intellisense") - .SetUrl("~/OrchardCore.Liquid/monaco/liquid-intellisense.js") - .SetDependencies("monaco") - .SetVersion("1.0.0"); + _manifest + .DefineScript("monaco-liquid-intellisense") + .SetUrl("~/OrchardCore.Liquid/monaco/liquid-intellisense.js") + .SetDependencies("monaco") + .SetVersion("1.0.0"); - _manifest - .DefineScript("liquid-intellisense") - .SetDependencies("monaco-liquid-intellisense") - .SetUrl("~/OrchardCore.Liquid/Scripts/liquid-intellisense.js"); - } + _manifest + .DefineScript("liquid-intellisense") + .SetDependencies("monaco-liquid-intellisense") + .SetUrl("~/OrchardCore.Liquid/Scripts/liquid-intellisense.js"); + } - public void Configure(ResourceManagementOptions options) - { - options.ResourceManifests.Add(_manifest); - } + public void Configure(ResourceManagementOptions options) + { + options.ResourceManifests.Add(_manifest); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs index a633f64c85b..fa1d29c6ced 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/ScriptsMiddleware.cs @@ -13,61 +13,60 @@ using OrchardCore.DisplayManagement.Liquid; using OrchardCore.Environment.Shell.Configuration; -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +public class ScriptsMiddleware { - public class ScriptsMiddleware - { - private readonly RequestDelegate _next; + private readonly RequestDelegate _next; - byte[] _bytes; - string _etag; + byte[] _bytes; + string _etag; - public ScriptsMiddleware(RequestDelegate next) - { - _next = next; - } + public ScriptsMiddleware(RequestDelegate next) + { + _next = next; + } - public async Task Invoke(HttpContext httpContext) + public async Task Invoke(HttpContext httpContext) + { + if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Liquid/Scripts", StringComparison.OrdinalIgnoreCase)) { - if (httpContext.Request.Path.StartsWithSegments("/OrchardCore.Liquid/Scripts", StringComparison.OrdinalIgnoreCase)) + if (Path.GetFileName(httpContext.Request.Path.Value) == "liquid-intellisense.js") { - if (Path.GetFileName(httpContext.Request.Path.Value) == "liquid-intellisense.js") + if (httpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var v)) { - if (httpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var v)) + if (v.Contains(_etag)) { - if (v.Contains(_etag)) - { - httpContext.Response.StatusCode = StatusCodes.Status304NotModified; - return; - } + httpContext.Response.StatusCode = StatusCodes.Status304NotModified; + return; } + } - var cacheControl = $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"; - if (_bytes == null) - { - var templateOptions = httpContext.RequestServices.GetRequiredService>(); - var liquidViewParser = httpContext.RequestServices.GetRequiredService(); - var shellConfiguration = httpContext.RequestServices.GetRequiredService(); - cacheControl = shellConfiguration.GetValue("StaticFileOptions:CacheControl", cacheControl); + var cacheControl = $"public, max-age={TimeSpan.FromDays(30).TotalSeconds}, s-max-age={TimeSpan.FromDays(365.25).TotalSeconds}"; + if (_bytes == null) + { + var templateOptions = httpContext.RequestServices.GetRequiredService>(); + var liquidViewParser = httpContext.RequestServices.GetRequiredService(); + var shellConfiguration = httpContext.RequestServices.GetRequiredService(); + cacheControl = shellConfiguration.GetValue("StaticFileOptions:CacheControl", cacheControl); - var filters = string.Join(',', templateOptions.Value.Filters.Select(x => $"'{x.Key}'")); - var tags = string.Join(',', liquidViewParser.RegisteredTags.Select(x => $"'{x.Key}'")); + var filters = string.Join(',', templateOptions.Value.Filters.Select(x => $"'{x.Key}'")); + var tags = string.Join(',', liquidViewParser.RegisteredTags.Select(x => $"'{x.Key}'")); - var script = $@"[{filters}].forEach(value=>{{if(!liquidFilters.includes(value)){{ liquidFilters.push(value);}}}}); + var script = $@"[{filters}].forEach(value=>{{if(!liquidFilters.includes(value)){{ liquidFilters.push(value);}}}}); [{tags}].forEach(value=>{{if(!liquidTags.includes(value)){{ liquidTags.push(value);}}}});"; - _etag = Guid.NewGuid().ToString("n"); - _bytes = Encoding.UTF8.GetBytes(script); - } - - httpContext.Response.Headers[HeaderNames.CacheControl] = cacheControl; - httpContext.Response.Headers[HeaderNames.ContentType] = "application/javascript"; - httpContext.Response.Headers[HeaderNames.ETag] = _etag; - await httpContext.Response.Body.WriteAsync(_bytes, httpContext?.RequestAborted ?? CancellationToken.None); - return; + _etag = Guid.NewGuid().ToString("n"); + _bytes = Encoding.UTF8.GetBytes(script); } + + httpContext.Response.Headers[HeaderNames.CacheControl] = cacheControl; + httpContext.Response.Headers[HeaderNames.ContentType] = "application/javascript"; + httpContext.Response.Headers[HeaderNames.ETag] = _etag; + await httpContext.Response.Body.WriteAsync(_bytes, httpContext?.RequestAborted ?? CancellationToken.None); + return; } - await _next.Invoke(httpContext); } + await _next.Invoke(httpContext); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Services/LiquidTemplateManager.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Services/LiquidTemplateManager.cs index eacc1ba7ddf..b122a15cc85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Services/LiquidTemplateManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Services/LiquidTemplateManager.cs @@ -11,131 +11,130 @@ using Microsoft.Extensions.Options; using OrchardCore.DisplayManagement.Liquid; -namespace OrchardCore.Liquid.Services +namespace OrchardCore.Liquid.Services; + +public class LiquidTemplateManager : ILiquidTemplateManager { - public class LiquidTemplateManager : ILiquidTemplateManager + private readonly IMemoryCache _memoryCache; + private readonly LiquidViewParser _liquidViewParser; + private readonly TemplateOptions _templateOptions; + private readonly IServiceProvider _serviceProvider; + + public LiquidTemplateManager( + IMemoryCache memoryCache, + LiquidViewParser liquidViewParser, + IOptions templateOptions, + IServiceProvider serviceProvider + ) { - private readonly IMemoryCache _memoryCache; - private readonly LiquidViewParser _liquidViewParser; - private readonly TemplateOptions _templateOptions; - private readonly IServiceProvider _serviceProvider; - - public LiquidTemplateManager( - IMemoryCache memoryCache, - LiquidViewParser liquidViewParser, - IOptions templateOptions, - IServiceProvider serviceProvider - ) + _memoryCache = memoryCache; + _liquidViewParser = liquidViewParser; + _templateOptions = templateOptions.Value; + _serviceProvider = serviceProvider; + } + + public Task RenderStringAsync(string source, TextEncoder encoder, object model = null, IEnumerable> properties = null) + { + if (string.IsNullOrWhiteSpace(source)) { - _memoryCache = memoryCache; - _liquidViewParser = liquidViewParser; - _templateOptions = templateOptions.Value; - _serviceProvider = serviceProvider; + return Task.FromResult((string)null); } - public Task RenderStringAsync(string source, TextEncoder encoder, object model = null, IEnumerable> properties = null) + var result = GetCachedTemplate(source); + var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); + + if (properties != null) { - if (string.IsNullOrWhiteSpace(source)) + foreach (var property in properties) { - return Task.FromResult((string)null); + context.SetValue(property.Key, property.Value); } + } - var result = GetCachedTemplate(source); - var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); - - if (properties != null) - { - foreach (var property in properties) - { - context.SetValue(property.Key, property.Value); - } - } + return result.RenderAsync(encoder, context, model); + } - return result.RenderAsync(encoder, context, model); + public async Task RenderHtmlContentAsync(string source, TextEncoder encoder, object model = null, IEnumerable> properties = null) + { + if (string.IsNullOrWhiteSpace(source)) + { + return HtmlString.Empty; } - public async Task RenderHtmlContentAsync(string source, TextEncoder encoder, object model = null, IEnumerable> properties = null) + var result = GetCachedTemplate(source); + var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); + + if (properties != null) { - if (string.IsNullOrWhiteSpace(source)) + foreach (var property in properties) { - return HtmlString.Empty; + context.SetValue(property.Key, property.Value); } + } - var result = GetCachedTemplate(source); - var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); + var htmlContentWriter = new ViewBufferTextWriterContent(); - if (properties != null) - { - foreach (var property in properties) - { - context.SetValue(property.Key, property.Value); - } - } - - var htmlContentWriter = new ViewBufferTextWriterContent(); + await result.RenderAsync(htmlContentWriter, encoder, context, model); - await result.RenderAsync(htmlContentWriter, encoder, context, model); + return htmlContentWriter; + } - return htmlContentWriter; + public Task RenderAsync(string source, TextWriter writer, TextEncoder encoder, object model = null, IEnumerable> properties = null) + { + if (string.IsNullOrWhiteSpace(source)) + { + return Task.CompletedTask; } - public Task RenderAsync(string source, TextWriter writer, TextEncoder encoder, object model = null, IEnumerable> properties = null) + var result = GetCachedTemplate(source); + var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); + + if (properties != null) { - if (string.IsNullOrWhiteSpace(source)) + foreach (var property in properties) { - return Task.CompletedTask; + context.SetValue(property.Key, property.Value); } + } - var result = GetCachedTemplate(source); - var context = new LiquidTemplateContext(_serviceProvider, _templateOptions); + return result.RenderAsync(writer, encoder, context, model); + } - if (properties != null) + public LiquidViewTemplate GetCachedTemplate(string source) + { + var errors = Enumerable.Empty(); + + var result = _memoryCache.GetOrCreate(source, (ICacheEntry e) => + { + if (!_liquidViewParser.TryParse(source, out var parsed, out var error)) { - foreach (var property in properties) - { - context.SetValue(property.Key, property.Value); - } + // If the source string cannot be parsed, create a template that contains the parser errors + _liquidViewParser.TryParse(string.Join(System.Environment.NewLine, errors), out parsed, out error); } - return result.RenderAsync(writer, encoder, context, model); - } - - public LiquidViewTemplate GetCachedTemplate(string source) - { - var errors = Enumerable.Empty(); + // Define a default sliding expiration to prevent the + // cache from being filled and still apply some micro-caching + // in case the template is used commonly + e.SetSlidingExpiration(TimeSpan.FromSeconds(30)); + return new LiquidViewTemplate(parsed); + }); - var result = _memoryCache.GetOrCreate(source, (ICacheEntry e) => - { - if (!_liquidViewParser.TryParse(source, out var parsed, out var error)) - { - // If the source string cannot be parsed, create a template that contains the parser errors - _liquidViewParser.TryParse(string.Join(System.Environment.NewLine, errors), out parsed, out error); - } - - // Define a default sliding expiration to prevent the - // cache from being filled and still apply some micro-caching - // in case the template is used commonly - e.SetSlidingExpiration(TimeSpan.FromSeconds(30)); - return new LiquidViewTemplate(parsed); - }); - - return result; - } + return result; + } - public bool Validate(string template, out IEnumerable errors) + public bool Validate(string template, out IEnumerable errors) + { + if (string.IsNullOrEmpty(template)) { - if (string.IsNullOrEmpty(template)) - { - errors = []; + errors = []; - return true; - } + return true; + } - var success = _liquidViewParser.TryParse(template, out _, out var error); + var success = _liquidViewParser.TryParse(template, out _, out var error); - errors = [error]; + errors = [error]; - return success; - } + return success; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs index 7f9e77ea78f..bbcb78b841e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/Startup.cs @@ -23,85 +23,84 @@ using OrchardCore.Modules; using OrchardCore.ResourceManagement; -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseMiddleware(); - } + app.UseMiddleware(); + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); - services.Configure(options => - { - options.Filters.AddFilter("t", LiquidViewFilters.Localize); - options.Filters.AddFilter("html_class", LiquidViewFilters.HtmlClass); - options.Filters.AddFilter("shape_properties", LiquidViewFilters.ShapeProperties); + services.Configure(options => + { + options.Filters.AddFilter("t", LiquidViewFilters.Localize); + options.Filters.AddFilter("html_class", LiquidViewFilters.HtmlClass); + options.Filters.AddFilter("shape_properties", LiquidViewFilters.ShapeProperties); - options.MemberAccessStrategy.Register(); + options.MemberAccessStrategy.Register(); - // Used to provide a factory to return a value based on a property name that is unknown at registration time. - options.MemberAccessStrategy.Register((obj, name) => obj.GetValueAsync(name)); + // Used to provide a factory to return a value based on a property name that is unknown at registration time. + options.MemberAccessStrategy.Register((obj, name) => obj.GetValueAsync(name)); - // When a property of a 'JsonObject' value is accessed, try to look into its properties. - options.MemberAccessStrategy.Register((source, name) => source[name]); + // When a property of a 'JsonObject' value is accessed, try to look into its properties. + options.MemberAccessStrategy.Register((source, name) => source[name]); - // Convert JToken to FluidValue - options.ValueConverters.Add(x => + // Convert JToken to FluidValue + options.ValueConverters.Add(x => + { + return x switch { - return x switch - { - JsonObject o => new ObjectValue(o), - JsonDynamicObject o => new ObjectValue((JsonObject)o), - JsonValue o => o.GetObjectValue(), - DateTime d => new ObjectValue(d), - _ => null - }; - }); + JsonObject o => new ObjectValue(o), + JsonDynamicObject o => new ObjectValue((JsonObject)o), + JsonValue o => o.GetObjectValue(), + DateTime d => new ObjectValue(d), + _ => null + }; + }); - options.Filters.AddFilter("json", JsonFilter.Json); - options.Filters.AddFilter("jsonparse", JsonParseFilter.JsonParse); - }) - .AddLiquidFilter("local") - .AddLiquidFilter("utc") - .AddLiquidFilter("slugify") - .AddLiquidFilter("liquid") - .AddLiquidFilter("href") - .AddLiquidFilter("absolute_url") - .AddLiquidFilter("shape_new") - .AddLiquidFilter("shape_render") - .AddLiquidFilter("shape_stringify"); + options.Filters.AddFilter("json", JsonFilter.Json); + options.Filters.AddFilter("jsonparse", JsonParseFilter.JsonParse); + }) + .AddLiquidFilter("local") + .AddLiquidFilter("utc") + .AddLiquidFilter("slugify") + .AddLiquidFilter("liquid") + .AddLiquidFilter("href") + .AddLiquidFilter("absolute_url") + .AddLiquidFilter("shape_new") + .AddLiquidFilter("shape_render") + .AddLiquidFilter("shape_stringify"); - services.AddTransient, ResourceManagementOptionsConfiguration>(); - } + services.AddTransient, ResourceManagementOptionsConfiguration>(); } +} - [RequireFeatures("OrchardCore.Contents")] - public sealed class LiquidPartStartup : StartupBase +[RequireFeatures("OrchardCore.Contents")] +public sealed class LiquidPartStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Liquid Part - services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Liquid Part + services.AddScoped(); + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddDataMigration(); - services.AddScoped(); - } + services.AddDataMigration(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Shortcodes")] - public sealed class ShortcodesStartup : StartupBase +[RequireFeatures("OrchardCore.Shortcodes")] +public sealed class ShortcodesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddLiquidFilter("shortcode"); - } + services.AddLiquidFilter("shortcode"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Liquid/ViewModels/LiquidPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Liquid/ViewModels/LiquidPartViewModel.cs index f7f46643ba5..02f25ef208a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Liquid/ViewModels/LiquidPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Liquid/ViewModels/LiquidPartViewModel.cs @@ -2,17 +2,16 @@ using OrchardCore.ContentManagement; using OrchardCore.Liquid.Models; -namespace OrchardCore.Liquid.ViewModels +namespace OrchardCore.Liquid.ViewModels; + +public class LiquidPartViewModel { - public class LiquidPartViewModel - { - public string Liquid { get; set; } - public string Html { get; set; } + public string Liquid { get; set; } + public string Html { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public LiquidPart LiquidPart { get; set; } - } + [BindNever] + public LiquidPart LiquidPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNode.cs b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNode.cs index c8a815fdf79..b6e93312613 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNode.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNode.cs @@ -1,12 +1,11 @@ using OrchardCore.AdminMenu.Models; -namespace OrchardCore.Lists.AdminNodes +namespace OrchardCore.Lists.AdminNodes; + +public class ListsAdminNode : AdminNode { - public class ListsAdminNode : AdminNode - { - public string ContentType { get; set; } - public bool AddContentTypeAsParent { get; set; } = true; - public string IconForParentLink { get; set; } - public string IconForContentItems { get; set; } - } + public string ContentType { get; set; } + public bool AddContentTypeAsParent { get; set; } = true; + public string IconForParentLink { get; set; } + public string IconForContentItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeDriver.cs index 5fb89288d2e..ee6b3a18ea8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeDriver.cs @@ -9,61 +9,60 @@ using OrchardCore.Lists.Models; using OrchardCore.Navigation; -namespace OrchardCore.Lists.AdminNodes +namespace OrchardCore.Lists.AdminNodes; + +public sealed class ListsAdminNodeDriver : DisplayDriver { - public sealed class ListsAdminNodeDriver : DisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public ListsAdminNodeDriver(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public ListsAdminNodeDriver(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public override Task DisplayAsync(ListsAdminNode treeNode, BuildDisplayContext context) - { - return CombineAsync( - View("ListsAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), - View("ListsAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") - ); - } + public override Task DisplayAsync(ListsAdminNode treeNode, BuildDisplayContext context) + { + return CombineAsync( + View("ListsAdminNode_Fields_TreeSummary", treeNode).Location("TreeSummary", "Content"), + View("ListsAdminNode_Fields_TreeThumbnail", treeNode).Location("TreeThumbnail", "Content") + ); + } - public override IDisplayResult Edit(ListsAdminNode treeNode, BuildEditorContext context) + public override IDisplayResult Edit(ListsAdminNode treeNode, BuildEditorContext context) + { + return Initialize("ListsAdminNode_Fields_TreeEdit", async model => { - return Initialize("ListsAdminNode_Fields_TreeEdit", async model => - { - model.ContentType = treeNode.ContentType; - model.ContentTypes = await GetContentTypesSelectListAsync(); - model.IconForContentItems = treeNode.IconForContentItems; - model.AddContentTypeAsParent = treeNode.AddContentTypeAsParent; - model.IconForParentLink = treeNode.IconForParentLink; - }).Location("Content"); - } + model.ContentType = treeNode.ContentType; + model.ContentTypes = await GetContentTypesSelectListAsync(); + model.IconForContentItems = treeNode.IconForContentItems; + model.AddContentTypeAsParent = treeNode.AddContentTypeAsParent; + model.IconForParentLink = treeNode.IconForParentLink; + }).Location("Content"); + } - public override async Task UpdateAsync(ListsAdminNode treeNode, UpdateEditorContext context) - { - var model = new ListsAdminNodeViewModel(); + public override async Task UpdateAsync(ListsAdminNode treeNode, UpdateEditorContext context) + { + var model = new ListsAdminNodeViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.ContentType, x => x.IconForContentItems, - m => m.AddContentTypeAsParent, - m => m.IconForParentLink); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.ContentType, x => x.IconForContentItems, + m => m.AddContentTypeAsParent, + m => m.IconForParentLink); - treeNode.ContentType = model.ContentType; - treeNode.IconForContentItems = model.IconForContentItems; - treeNode.AddContentTypeAsParent = model.AddContentTypeAsParent; - treeNode.IconForParentLink = model.IconForParentLink; + treeNode.ContentType = model.ContentType; + treeNode.IconForContentItems = model.IconForContentItems; + treeNode.AddContentTypeAsParent = model.AddContentTypeAsParent; + treeNode.IconForParentLink = model.IconForParentLink; - return Edit(treeNode, context); - } + return Edit(treeNode, context); + } - private async Task> GetContentTypesSelectListAsync() - { - return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(ctd => ctd.Parts.Any(p => p.PartDefinition.Name.Equals(nameof(ListPart), StringComparison.OrdinalIgnoreCase))) - .OrderBy(ctd => ctd.DisplayName) - .Select(ctd => new SelectListItem { Value = ctd.Name, Text = ctd.DisplayName }) - .ToList(); - } + private async Task> GetContentTypesSelectListAsync() + { + return (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(ctd => ctd.Parts.Any(p => p.PartDefinition.Name.Equals(nameof(ListPart), StringComparison.OrdinalIgnoreCase))) + .OrderBy(ctd => ctd.DisplayName) + .Select(ctd => new SelectListItem { Value = ctd.Name, Text = ctd.DisplayName }) + .ToList(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeNavigationBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeNavigationBuilder.cs index 0a111d6684a..3262118a3c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeNavigationBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeNavigationBuilder.cs @@ -14,121 +14,120 @@ using OrchardCore.Navigation; using YesSql; -namespace OrchardCore.Lists.AdminNodes +namespace OrchardCore.Lists.AdminNodes; + +public class ListsAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder { - public class ListsAdminNodeNavigationBuilder : IAdminNodeNavigationBuilder + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentManager _contentManager; + private readonly ISession _session; + private readonly ILogger _logger; + private ListsAdminNode _node; + private ContentTypeDefinition _contentType; + + // Security check. + private const int MaxItemsInNode = 100; + + public ListsAdminNodeNavigationBuilder( + IContentDefinitionManager contentDefinitionManager, + IContentManager contentManager, + ISession session, + ILogger logger) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContentManager _contentManager; - private readonly ISession _session; - private readonly ILogger _logger; - private ListsAdminNode _node; - private ContentTypeDefinition _contentType; - - // Security check. - private const int MaxItemsInNode = 100; - - public ListsAdminNodeNavigationBuilder( - IContentDefinitionManager contentDefinitionManager, - IContentManager contentManager, - ISession session, - ILogger logger) + _contentDefinitionManager = contentDefinitionManager; + _contentManager = contentManager; + _session = session; + _logger = logger; + } + + public string Name => nameof(ListsAdminNode); + + public async Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + { + _node = menuItem as ListsAdminNode; + + if (_node == null || !_node.Enabled || string.IsNullOrEmpty(_node.ContentType)) { - _contentDefinitionManager = contentDefinitionManager; - _contentManager = contentManager; - _session = session; - _logger = logger; + return; } - public string Name => nameof(ListsAdminNode); + _contentType = await _contentDefinitionManager.GetTypeDefinitionAsync(_node.ContentType); - public async Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, IEnumerable treeNodeBuilders) + if (_node.AddContentTypeAsParent) { - _node = menuItem as ListsAdminNode; - - if (_node == null || !_node.Enabled || string.IsNullOrEmpty(_node.ContentType)) + if (_contentType == null) { - return; + _logger.LogError("Can't find The content type '{ContentType}' for list admin node.", _node.ContentType); } - _contentType = await _contentDefinitionManager.GetTypeDefinitionAsync(_node.ContentType); - - if (_node.AddContentTypeAsParent) + await builder.AddAsync(new LocalizedString(_contentType.DisplayName, _contentType.DisplayName), async listTypeMenu => { - if (_contentType == null) - { - _logger.LogError("Can't find The content type '{ContentType}' for list admin node.", _node.ContentType); - } + AddPrefixToClasses(_node.IconForParentLink).ForEach(c => listTypeMenu.AddClass(c)); + listTypeMenu.Permission(ContentTypePermissionsHelper.CreateDynamicPermission( + ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditContent.Name], _contentType)); + await AddContentItemsAsync(listTypeMenu); + }); + } + else + { + await AddContentItemsAsync(builder); + } - await builder.AddAsync(new LocalizedString(_contentType.DisplayName, _contentType.DisplayName), async listTypeMenu => - { - AddPrefixToClasses(_node.IconForParentLink).ForEach(c => listTypeMenu.AddClass(c)); - listTypeMenu.Permission(ContentTypePermissionsHelper.CreateDynamicPermission( - ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditContent.Name], _contentType)); - await AddContentItemsAsync(listTypeMenu); - }); - } - else + // Add external children. + foreach (var childNode in _node.Items) + { + try { - await AddContentItemsAsync(builder); + var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childNode.GetType().Name); + await treeBuilder.BuildNavigationAsync(childNode, builder, treeNodeBuilders); } - - // Add external children. - foreach (var childNode in _node.Items) + catch (Exception e) { - try - { - var treeBuilder = treeNodeBuilders.FirstOrDefault(x => x.Name == childNode.GetType().Name); - await treeBuilder.BuildNavigationAsync(childNode, builder, treeNodeBuilders); - } - catch (Exception e) - { - _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childNode.GetType().Name); - } + _logger.LogError(e, "An exception occurred while building the '{MenuItem}' child Menu Item.", childNode.GetType().Name); } } + } - private async Task AddContentItemsAsync(NavigationBuilder listTypeMenu) + private async Task AddContentItemsAsync(NavigationBuilder listTypeMenu) + { + foreach (var ci in await GetContentItemsAsync()) { - foreach (var ci in await GetContentItemsAsync()) - { - var cim = await _contentManager.PopulateAspectAsync(ci); + var cim = await _contentManager.PopulateAspectAsync(ci); - if (cim.AdminRouteValues.Count > 0 && ci.DisplayText != null) + if (cim.AdminRouteValues.Count > 0 && ci.DisplayText != null) + { + listTypeMenu.Add(new LocalizedString(ci.DisplayText, ci.DisplayText), m => { - listTypeMenu.Add(new LocalizedString(ci.DisplayText, ci.DisplayText), m => - { - m.Action(cim.AdminRouteValues["Action"] as string, cim.AdminRouteValues["Controller"] as string, cim.AdminRouteValues); - m.Resource(ci); - m.Priority(_node.Priority); - m.Position(_node.Position); - m.LocalNav(); - AddPrefixToClasses(_node.IconForContentItems).ToList().ForEach(c => m.AddClass(c)); - - m.Permission(ContentTypePermissionsHelper.CreateDynamicPermission( - ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditContent.Name], _contentType)); - }); - } + m.Action(cim.AdminRouteValues["Action"] as string, cim.AdminRouteValues["Controller"] as string, cim.AdminRouteValues); + m.Resource(ci); + m.Priority(_node.Priority); + m.Position(_node.Position); + m.LocalNav(); + AddPrefixToClasses(_node.IconForContentItems).ToList().ForEach(c => m.AddClass(c)); + + m.Permission(ContentTypePermissionsHelper.CreateDynamicPermission( + ContentTypePermissionsHelper.PermissionTemplates[CommonPermissions.EditContent.Name], _contentType)); + }); } } + } - private async Task> GetContentItemsAsync() - { - return (await _session.Query() - .With(x => x.Latest && x.ContentType == _node.ContentType) - .Take(MaxItemsInNode) - .ListAsync()) - .OrderBy(x => x.DisplayText) - .ToList(); - } + private async Task> GetContentItemsAsync() + { + return (await _session.Query() + .With(x => x.Latest && x.ContentType == _node.ContentType) + .Take(MaxItemsInNode) + .ListAsync()) + .OrderBy(x => x.DisplayText) + .ToList(); + } - private static List AddPrefixToClasses(string unprefixed) - { - return unprefixed?.Split(' ') - .ToList() - .Select(c => "icon-class-" + c) - .ToList() - ?? []; - } + private static List AddPrefixToClasses(string unprefixed) + { + return unprefixed?.Split(' ') + .ToList() + .Select(c => "icon-class-" + c) + .ToList() + ?? []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeViewModel.cs index bf3fafda8f7..e444a89b358 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/AdminNodes/ListsAdminNodeViewModel.cs @@ -1,14 +1,13 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Lists.AdminNodes +namespace OrchardCore.Lists.AdminNodes; + +public class ListsAdminNodeViewModel { - public class ListsAdminNodeViewModel - { - public string ContentType { get; set; } - public bool AddContentTypeAsParent { get; set; } - public string IconForParentLink { get; set; } - public string IconForContentItems { get; set; } - public List ContentTypes { get; set; } - } + public string ContentType { get; set; } + public bool AddContentTypeAsParent { get; set; } + public string IconForParentLink { get; set; } + public string IconForContentItems { get; set; } + public List ContentTypes { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/OrderController.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/OrderController.cs index 8564d23fd04..e93bd56a8d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/OrderController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/OrderController.cs @@ -9,81 +9,80 @@ using OrchardCore.Lists.Services; using OrchardCore.Navigation; -namespace OrchardCore.Lists.Controllers +namespace OrchardCore.Lists.Controllers; + +[Admin] +public class OrderController : Controller { - [Admin] - public class OrderController : Controller + private readonly IContainerService _containerService; + private readonly IAuthorizationService _authorizationService; + + public OrderController(IContainerService containerService, IAuthorizationService authorizationService) { - private readonly IContainerService _containerService; - private readonly IAuthorizationService _authorizationService; + _containerService = containerService; + _authorizationService = authorizationService; + } - public OrderController(IContainerService containerService, IAuthorizationService authorizationService) + [HttpPost] + [Admin("Lists/Order/{containerId?}", "ListOrder")] + public async Task UpdateContentItemOrders(string containerId, int oldIndex, int newIndex, PagerSlimParameters pagerSlimParameters, int pageSize) + { + var pager = new PagerSlim(pagerSlimParameters, pageSize); + // Reverse pager as it represents the next page(s), rather than current page + if (pager.Before != null && pager.After != null) { - _containerService = containerService; - _authorizationService = authorizationService; + var beforeValue = int.Parse(pager.Before); + beforeValue -= 1; + var afterValue = int.Parse(pager.After); + afterValue += 1; + pager.Before = afterValue.ToString(); + pager.After = beforeValue.ToString(); } - - [HttpPost] - [Admin("Lists/Order/{containerId?}", "ListOrder")] - public async Task UpdateContentItemOrders(string containerId, int oldIndex, int newIndex, PagerSlimParameters pagerSlimParameters, int pageSize) + else if (pager.Before != null) { - var pager = new PagerSlim(pagerSlimParameters, pageSize); - // Reverse pager as it represents the next page(s), rather than current page - if (pager.Before != null && pager.After != null) - { - var beforeValue = int.Parse(pager.Before); - beforeValue -= 1; - var afterValue = int.Parse(pager.After); - afterValue += 1; - pager.Before = afterValue.ToString(); - pager.After = beforeValue.ToString(); - } - else if (pager.Before != null) - { - var beforeValue = int.Parse(pager.Before); - beforeValue -= 1; - pager.Before = null; - pager.After = beforeValue.ToString(); - } - else if (pager.After != null) - { - var afterValue = int.Parse(pager.After); - afterValue += 1; - pager.After = null; - pager.Before = afterValue.ToString(); - } + var beforeValue = int.Parse(pager.Before); + beforeValue -= 1; + pager.Before = null; + pager.After = beforeValue.ToString(); + } + else if (pager.After != null) + { + var afterValue = int.Parse(pager.After); + afterValue += 1; + pager.After = null; + pager.Before = afterValue.ToString(); + } - // Include draft items. - var pageOfContentItems = (await _containerService.QueryContainedItemsAsync( - containerId, - true, - pager, - new ContainedItemOptions { Status = ContentsStatus.Latest })) - .ToList(); + // Include draft items. + var pageOfContentItems = (await _containerService.QueryContainedItemsAsync( + containerId, + true, + pager, + new ContainedItemOptions { Status = ContentsStatus.Latest })) + .ToList(); - if (pageOfContentItems == null || pageOfContentItems.Count == 0) - { - return NotFound(); - } + if (pageOfContentItems == null || pageOfContentItems.Count == 0) + { + return NotFound(); + } - foreach (var pagedContentItem in pageOfContentItems) + foreach (var pagedContentItem in pageOfContentItems) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, pagedContentItem)) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.PublishContent, pagedContentItem)) - { - return Forbid(); - } + return Forbid(); } + } - var currentOrderOfFirstItem = pageOfContentItems.FirstOrDefault().As().Order; + var currentOrderOfFirstItem = pageOfContentItems.FirstOrDefault().As().Order; - var contentItem = pageOfContentItems[oldIndex]; + var contentItem = pageOfContentItems[oldIndex]; - pageOfContentItems.Remove(contentItem); - pageOfContentItems.Insert(newIndex, contentItem); + pageOfContentItems.Remove(contentItem); + pageOfContentItems.Insert(newIndex, contentItem); - await _containerService.UpdateContentItemOrdersAsync(pageOfContentItems, currentOrderOfFirstItem); + await _containerService.UpdateContentItemOrdersAsync(pageOfContentItems, currentOrderOfFirstItem); - return Ok(); - } + return Ok(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/RemotePublishingController.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/RemotePublishingController.cs index 80e6ebdae9a..90c3515599e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/RemotePublishingController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Controllers/RemotePublishingController.cs @@ -6,65 +6,64 @@ using OrchardCore.Lists.Models; using OrchardCore.Modules; -namespace OrchardCore.Lists.Controllers +namespace OrchardCore.Lists.Controllers; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public class RemotePublishingController : Controller { - [RequireFeatures("OrchardCore.RemotePublishing")] - public class RemotePublishingController : Controller + private readonly IContentManager _contentManager; + private readonly ILogger _logger; + + public RemotePublishingController( + IContentManager contentManager, + ILogger logger) { - private readonly IContentManager _contentManager; - private readonly ILogger _logger; + _contentManager = contentManager; + _logger = logger; + } - public RemotePublishingController( - IContentManager contentManager, - ILogger logger) + public async Task Rsd(string contentItemId) + { + if (_logger.IsEnabled(LogLevel.Debug)) { - _contentManager = contentManager; - _logger = logger; + _logger.LogDebug("RSD requested"); } - public async Task Rsd(string contentItemId) - { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("RSD requested"); - } - - var contentItem = await _contentManager.GetAsync(contentItemId); + var contentItem = await _contentManager.GetAsync(contentItemId); - if (contentItem == null) - { - return NotFound(); - } + if (contentItem == null) + { + return NotFound(); + } - var listPart = contentItem.As(); + var listPart = contentItem.As(); - if (listPart == null) - { - return NotFound(); - } + if (listPart == null) + { + return NotFound(); + } - const string manifestUri = "http://archipelago.phrasewise.com/rsd"; + const string manifestUri = "http://archipelago.phrasewise.com/rsd"; - var url = Url.Action("Index", "Home", new { area = "OrchardCore.XmlRpc" }, Request.Scheme); + var url = Url.Action("Index", "Home", new { area = "OrchardCore.XmlRpc" }, Request.Scheme); - var options = new XElement( - XName.Get("service", manifestUri), - new XElement(XName.Get("engineName", manifestUri), "Orchard CMS"), - new XElement(XName.Get("engineLink", manifestUri), "https://orchardcore.net"), - new XElement(XName.Get("homePageLink", manifestUri), "https://orchardcore.net"), - new XElement(XName.Get("apis", manifestUri), - new XElement(XName.Get("api", manifestUri), - new XAttribute("name", "MetaWeblog"), - new XAttribute("preferred", true), - new XAttribute("apiLink", url), - new XAttribute("blogID", contentItem.ContentItemId)))); + var options = new XElement( + XName.Get("service", manifestUri), + new XElement(XName.Get("engineName", manifestUri), "Orchard CMS"), + new XElement(XName.Get("engineLink", manifestUri), "https://orchardcore.net"), + new XElement(XName.Get("homePageLink", manifestUri), "https://orchardcore.net"), + new XElement(XName.Get("apis", manifestUri), + new XElement(XName.Get("api", manifestUri), + new XAttribute("name", "MetaWeblog"), + new XAttribute("preferred", true), + new XAttribute("apiLink", url), + new XAttribute("blogID", contentItem.ContentItemId)))); - var doc = new XDocument(new XElement( - XName.Get("rsd", manifestUri), - new XAttribute("version", "1.0"), - options)); + var doc = new XDocument(new XElement( + XName.Get("rsd", manifestUri), + new XAttribute("version", "1.0"), + options)); - return Content(doc.ToString(), "text/xml"); - } + return Content(doc.ToString(), "text/xml"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ContainedPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ContainedPartDisplayDriver.cs index a2142388641..245da4828e1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ContainedPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ContainedPartDisplayDriver.cs @@ -11,177 +11,176 @@ using OrchardCore.Lists.Services; using OrchardCore.Lists.ViewModels; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public sealed class ContainedPartDisplayDriver : ContentDisplayDriver { - public sealed class ContainedPartDisplayDriver : ContentDisplayDriver + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContainerService _containerService; + + public ContainedPartDisplayDriver( + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + IContainerService containerService + ) { - private readonly IContentManager _contentManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContainerService _containerService; - - public ContainedPartDisplayDriver( - IContentManager contentManager, - IContentDefinitionManager contentDefinitionManager, - IContainerService containerService - ) - { - _contentManager = contentManager; - _contentDefinitionManager = contentDefinitionManager; - _containerService = containerService; - } + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _containerService = containerService; + } - public override async Task EditAsync(ContentItem model, BuildEditorContext context) + public override async Task EditAsync(ContentItem model, BuildEditorContext context) + { + // This method can get called when a new content item is created, at that point + // the query string contains a ListPart.ContainerId value, or when an + // existing content item has ContainedPart value. In both cases the hidden field + // needs to be rendered in the edit form to maintain the relationship with the parent. + var containedPart = model.As(); + + if (containedPart != null) { - // This method can get called when a new content item is created, at that point - // the query string contains a ListPart.ContainerId value, or when an - // existing content item has ContainedPart value. In both cases the hidden field - // needs to be rendered in the edit form to maintain the relationship with the parent. - var containedPart = model.As(); + return await BuildViewModelAsync(containedPart.ListContentItemId, containedPart.ListContentType, model.ContentType); + } - if (containedPart != null) - { - return await BuildViewModelAsync(containedPart.ListContentItemId, containedPart.ListContentType, model.ContentType); - } + var viewModel = new EditContainedPartViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); - var viewModel = new EditContainedPartViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); + if (viewModel.ContainerId != null && viewModel.ContentType == model.ContentType) + { + // We are creating a content item that needs to be added to a container. + // Render the container id as part of the form. The content type, and the enable ordering setting. + // The content type must be included to prevent any contained items, + // such as widgets, from also having a ContainedPart shape built for them. - if (viewModel.ContainerId != null && viewModel.ContentType == model.ContentType) + // Attach ContainedPart to the content item during edit to provide handlers container info. + await model.AlterAsync(async part => { - // We are creating a content item that needs to be added to a container. - // Render the container id as part of the form. The content type, and the enable ordering setting. - // The content type must be included to prevent any contained items, - // such as widgets, from also having a ContainedPart shape built for them. - - // Attach ContainedPart to the content item during edit to provide handlers container info. - await model.AlterAsync(async part => + part.ListContentItemId = viewModel.ContainerId; + part.ListContentType = viewModel.ContainerContentType; + if (viewModel.EnableOrdering) { - part.ListContentItemId = viewModel.ContainerId; - part.ListContentType = viewModel.ContainerContentType; - if (viewModel.EnableOrdering) - { - part.Order = await _containerService.GetNextOrderNumberAsync(viewModel.ContainerId); - } - }); - - return await BuildViewModelAsync(viewModel.ContainerId, viewModel.ContainerContentType, model.ContentType, viewModel.EnableOrdering); - } + part.Order = await _containerService.GetNextOrderNumberAsync(viewModel.ContainerId); + } + }); - return null; + return await BuildViewModelAsync(viewModel.ContainerId, viewModel.ContainerContentType, model.ContentType, viewModel.EnableOrdering); } - public override async Task UpdateAsync(ContentItem model, UpdateEditorContext context) - { - var viewModel = new EditContainedPartViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); + return null; + } - // The content type must match the value provided in the query string - // in order for the ContainedPart to be included on the Content Item. - if (viewModel.ContainerId != null && viewModel.ContentType == model.ContentType) - { - await model.AlterAsync(async part => - { - part.ListContentItemId = viewModel.ContainerId; - part.ListContentType = viewModel.ContainerContentType; + public override async Task UpdateAsync(ContentItem model, UpdateEditorContext context) + { + var viewModel = new EditContainedPartViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); - // If creating get next order number so item is added to the end of the list. - if (viewModel.EnableOrdering) - { - part.Order = await _containerService.GetNextOrderNumberAsync(viewModel.ContainerId); - } - }); - } + // The content type must match the value provided in the query string + // in order for the ContainedPart to be included on the Content Item. + if (viewModel.ContainerId != null && viewModel.ContentType == model.ContentType) + { + await model.AlterAsync(async part => + { + part.ListContentItemId = viewModel.ContainerId; + part.ListContentType = viewModel.ContainerContentType; - return await EditAsync(model, context); + // If creating get next order number so item is added to the end of the list. + if (viewModel.EnableOrdering) + { + part.Order = await _containerService.GetNextOrderNumberAsync(viewModel.ContainerId); + } + }); } - private async Task BuildViewModelAsync(string containerId, string containerContentType, string contentType, bool enableOrdering = false) + return await EditAsync(model, context); + } + + private async Task BuildViewModelAsync(string containerId, string containerContentType, string contentType, bool enableOrdering = false) + { + var results = new List() { - var results = new List() + Initialize("ListPart_ContainerId", m => { - Initialize("ListPart_ContainerId", m => - { - m.ContainerId = containerId; - m.ContainerContentType = containerContentType; - m.EnableOrdering = enableOrdering; - m.ContentType = contentType; - }) - .Location("Content"), - }; - - if (!string.IsNullOrEmpty(containerContentType)) + m.ContainerId = containerId; + m.ContainerContentType = containerContentType; + m.EnableOrdering = enableOrdering; + m.ContentType = contentType; + }) + .Location("Content"), + }; + + if (!string.IsNullOrEmpty(containerContentType)) + { + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(containerContentType); + + if (definition != null) { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(containerContentType); + var listPart = definition.Parts.FirstOrDefault(x => x.PartDefinition.Name == nameof(ListPart)); + var settings = listPart?.GetSettings(); - if (definition != null) + if (settings != null) { - var listPart = definition.Parts.FirstOrDefault(x => x.PartDefinition.Name == nameof(ListPart)); - var settings = listPart?.GetSettings(); + var container = await GetContainerAsync(containerId); - if (settings != null) + if (container != null) { - var container = await GetContainerAsync(containerId); + // Add list part navigation. + results.Add(Initialize("ListPartNavigationAdmin", async model => + { + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); + model.Container = container; + model.EnableOrdering = settings.EnableOrdering; + model.ContainerContentTypeDefinition = definition; + }).Location("Content:1.5")); - if (container != null) + if (settings.ShowHeader) { - // Add list part navigation. - results.Add(Initialize("ListPartNavigationAdmin", async model => - { - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); - model.Container = container; - model.EnableOrdering = settings.EnableOrdering; - model.ContainerContentTypeDefinition = definition; - }).Location("Content:1.5")); - - if (settings.ShowHeader) - { - results.Add(GetListPartHeader(container, settings)); - } + results.Add(GetListPartHeader(container, settings)); } } } } - - return Combine(results); } - private ShapeResult GetListPartHeader(ContentItem containerContentItem, ListPartSettings listPartSettings) - => Initialize("ListPartHeaderAdmin", async model => - { - model.ContainerContentItem = containerContentItem; - - if (listPartSettings != null) - { - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(listPartSettings)).ToArray(); - model.EnableOrdering = listPartSettings.EnableOrdering; - } - }).Location("Content:1"); - - // Initially, attempt to locate a published container. - // If none is found, try acquiring the most recent unpublished version. - private async Task GetContainerAsync(string containerId) - => await _contentManager.GetAsync(containerId) ?? await _contentManager.GetAsync(containerId, VersionOptions.Latest); + return Combine(results); + } - private async Task> GetContainedContentTypesAsync(ListPartSettings settings) + private ShapeResult GetListPartHeader(ContentItem containerContentItem, ListPartSettings listPartSettings) + => Initialize("ListPartHeaderAdmin", async model => { - if (settings.ContainedContentTypes == null) + model.ContainerContentItem = containerContentItem; + + if (listPartSettings != null) { - return []; + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(listPartSettings)).ToArray(); + model.EnableOrdering = listPartSettings.EnableOrdering; } + }).Location("Content:1"); - var definitions = new List(); + // Initially, attempt to locate a published container. + // If none is found, try acquiring the most recent unpublished version. + private async Task GetContainerAsync(string containerId) + => await _contentManager.GetAsync(containerId) ?? await _contentManager.GetAsync(containerId, VersionOptions.Latest); - foreach (var contentTypeDefinition in settings.ContainedContentTypes) - { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentTypeDefinition); + private async Task> GetContainedContentTypesAsync(ListPartSettings settings) + { + if (settings.ContainedContentTypes == null) + { + return []; + } - if (definition is not null) - { - definitions.Add(definition); - } - } + var definitions = new List(); + + foreach (var contentTypeDefinition in settings.ContainedContentTypes) + { + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentTypeDefinition); - return definitions; + if (definition is not null) + { + definitions.Add(definition); + } } + + return definitions; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartContentsAdminListDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartContentsAdminListDisplayDriver.cs index ff5460a7909..29ac1341ba8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartContentsAdminListDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartContentsAdminListDisplayDriver.cs @@ -5,36 +5,35 @@ using OrchardCore.Lists.Models; using OrchardCore.Lists.ViewModels; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public sealed class ListPartContentsAdminListDisplayDriver : DisplayDriver { - public sealed class ListPartContentsAdminListDisplayDriver : DisplayDriver + protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) { - protected override void BuildPrefix(ContentOptionsViewModel model, string htmlFieldPrefix) - { - Prefix = "ListPart"; - } + Prefix = "ListPart"; + } + + public override IDisplayResult Edit(ContentOptionsViewModel model, BuildEditorContext context) + { + return Dynamic("ContentsAdminList__ListPartFilter").Location("Actions:20"); + } - public override IDisplayResult Edit(ContentOptionsViewModel model, BuildEditorContext context) + public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + { + var viewModel = new ListPartContentsAdminFilterViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); + + if (viewModel.ShowListContentTypes) { - return Dynamic("ContentsAdminList__ListPartFilter").Location("Actions:20"); + model.RouteValues.TryAdd("ListPart.ShowListContentTypes", viewModel.ShowListContentTypes); } - public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + if (!string.IsNullOrEmpty(viewModel.ListContentItemId)) { - var viewModel = new ListPartContentsAdminFilterViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); - - if (viewModel.ShowListContentTypes) - { - model.RouteValues.TryAdd("ListPart.ShowListContentTypes", viewModel.ShowListContentTypes); - } - - if (!string.IsNullOrEmpty(viewModel.ListContentItemId)) - { - model.RouteValues.TryAdd("ListPart.ListContentItemId", viewModel.ListContentItemId); - } - - return Edit(model, context); + model.RouteValues.TryAdd("ListPart.ListContentItemId", viewModel.ListContentItemId); } + + return Edit(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartDisplayDriver.cs index 8f295140ec5..be5970901fc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Drivers/ListPartDisplayDriver.cs @@ -14,182 +14,181 @@ using OrchardCore.Lists.ViewModels; using OrchardCore.Navigation; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public sealed class ListPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ListPartDisplayDriver : ContentPartDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContainerService _containerService; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public ListPartDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + IContainerService containerService, + IUpdateModelAccessor updateModelAccessor + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContainerService _containerService; - private readonly IUpdateModelAccessor _updateModelAccessor; - - public ListPartDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - IContainerService containerService, - IUpdateModelAccessor updateModelAccessor - ) - { - _contentDefinitionManager = contentDefinitionManager; - _containerService = containerService; - _updateModelAccessor = updateModelAccessor; - } - - public override IDisplayResult Edit(ListPart part, BuildPartEditorContext context) - { - var settings = context.TypePartDefinition.GetSettings(); - - return - Combine( - InitializeEditListPartNavigationAdmin(part, context, settings), - InitializeEditListPartHeaderAdmin(part, context, settings) - ); - } + _contentDefinitionManager = contentDefinitionManager; + _containerService = containerService; + _updateModelAccessor = updateModelAccessor; + } - public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) - { - var settings = context.TypePartDefinition.GetSettings(); + public override IDisplayResult Edit(ListPart part, BuildPartEditorContext context) + { + var settings = context.TypePartDefinition.GetSettings(); - return - Combine( - InitializeDisplayListPartDisplayShape(listPart, context), - InitializeDisplayListPartDetailAdminShape(listPart, context), - InitializeDisplayListPartNavigationAdminShape(listPart, context, settings), - InitializeDisplayListPartHeaderAdminShape(listPart, settings), - InitializeDisplayListPartSummaryAdmin(listPart) - ); - } + return + Combine( + InitializeEditListPartNavigationAdmin(part, context, settings), + InitializeEditListPartHeaderAdmin(part, context, settings) + ); + } - private ShapeResult InitializeEditListPartHeaderAdmin(ListPart part, BuildPartEditorContext context, ListPartSettings settings) - { - return Initialize("ListPartHeaderAdmin", async model => - { - model.ContainerContentItem = part.ContentItem; - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); - model.EnableOrdering = settings.EnableOrdering; - }).Location("Content:1") - .RenderWhen(() => Task.FromResult(!context.IsNew && settings.ShowHeader)); - } + public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) + { + var settings = context.TypePartDefinition.GetSettings(); + + return + Combine( + InitializeDisplayListPartDisplayShape(listPart, context), + InitializeDisplayListPartDetailAdminShape(listPart, context), + InitializeDisplayListPartNavigationAdminShape(listPart, context, settings), + InitializeDisplayListPartHeaderAdminShape(listPart, settings), + InitializeDisplayListPartSummaryAdmin(listPart) + ); + } - private ShapeResult InitializeEditListPartNavigationAdmin(ListPart part, BuildPartEditorContext context, ListPartSettings settings) + private ShapeResult InitializeEditListPartHeaderAdmin(ListPart part, BuildPartEditorContext context, ListPartSettings settings) + { + return Initialize("ListPartHeaderAdmin", async model => { - return Initialize("ListPartNavigationAdmin", async model => - { - model.Container = part.ContentItem; - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); - model.EnableOrdering = settings.EnableOrdering; - model.ContainerContentTypeDefinition = context.TypePartDefinition.ContentTypeDefinition; - }) - .Location("Content:1.5") - .RenderWhen(() => Task.FromResult(!context.IsNew)); - } + model.ContainerContentItem = part.ContentItem; + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); + model.EnableOrdering = settings.EnableOrdering; + }).Location("Content:1") + .RenderWhen(() => Task.FromResult(!context.IsNew && settings.ShowHeader)); + } - private ShapeResult InitializeDisplayListPartSummaryAdmin(ListPart listPart) + private ShapeResult InitializeEditListPartNavigationAdmin(ListPart part, BuildPartEditorContext context, ListPartSettings settings) + { + return Initialize("ListPartNavigationAdmin", async model => { - return Initialize("ListPartSummaryAdmin", model => model.ContentItem = listPart.ContentItem) - .Location("SummaryAdmin", "Actions:4"); - } + model.Container = part.ContentItem; + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); + model.EnableOrdering = settings.EnableOrdering; + model.ContainerContentTypeDefinition = context.TypePartDefinition.ContentTypeDefinition; + }) + .Location("Content:1.5") + .RenderWhen(() => Task.FromResult(!context.IsNew)); + } - private ShapeResult InitializeDisplayListPartHeaderAdminShape(ListPart listPart, ListPartSettings settings) - { - return Initialize("ListPartHeaderAdmin", async model => - { - model.ContainerContentItem = listPart.ContentItem; - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); - model.EnableOrdering = settings.EnableOrdering; - }).Location("DetailAdmin", "Content:1") - .RenderWhen(() => Task.FromResult(settings.ShowHeader)); - } + private ShapeResult InitializeDisplayListPartSummaryAdmin(ListPart listPart) + { + return Initialize("ListPartSummaryAdmin", model => model.ContentItem = listPart.ContentItem) + .Location("SummaryAdmin", "Actions:4"); + } - private ShapeResult InitializeDisplayListPartNavigationAdminShape(ListPart listPart, BuildPartDisplayContext context, ListPartSettings settings) + private ShapeResult InitializeDisplayListPartHeaderAdminShape(ListPart listPart, ListPartSettings settings) + { + return Initialize("ListPartHeaderAdmin", async model => { - return Initialize("ListPartNavigationAdmin", async model => - { - model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); - model.Container = listPart.ContentItem; - model.EnableOrdering = settings.EnableOrdering; - model.ContainerContentTypeDefinition = context.TypePartDefinition.ContentTypeDefinition; - }).Location("DetailAdmin", "Content:1.5"); - } + model.ContainerContentItem = listPart.ContentItem; + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); + model.EnableOrdering = settings.EnableOrdering; + }).Location("DetailAdmin", "Content:1") + .RenderWhen(() => Task.FromResult(settings.ShowHeader)); + } - private ShapeResult InitializeDisplayListPartDetailAdminShape(ListPart listPart, BuildPartDisplayContext context) + private ShapeResult InitializeDisplayListPartNavigationAdminShape(ListPart listPart, BuildPartDisplayContext context, ListPartSettings settings) + { + return Initialize("ListPartNavigationAdmin", async model => { - return Initialize("ListPartDetailAdmin", (Func)(async model => - { - var pager = await GetPagerSlimAsync(context); - var settings = context.TypePartDefinition.GetSettings(); - var listPartFilterViewModel = new ListPartFilterViewModel(); - var containedItemOptions = new ContainedItemOptions(); - - await _updateModelAccessor.ModelUpdater.TryUpdateModelAsync(listPartFilterViewModel, Prefix); - model.ListPart = listPart; - containedItemOptions.DisplayText = listPartFilterViewModel.DisplayText; - containedItemOptions.Status = listPartFilterViewModel.Status; - model.ListPartFilterViewModel = listPartFilterViewModel; - - model.ContentItems = (await _containerService.QueryContainedItemsAsync( - listPart.ContentItem.ContentItemId, - settings.EnableOrdering, - pager, - containedItemOptions)).ToArray(); - - model.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(settings); - model.Context = context; - model.EnableOrdering = settings.EnableOrdering; - model.Pager = await context.New.PagerSlim(pager); - })) - .Location("DetailAdmin", "Content:10"); - } + model.ContainedContentTypeDefinitions = (await GetContainedContentTypesAsync(settings)).ToArray(); + model.Container = listPart.ContentItem; + model.EnableOrdering = settings.EnableOrdering; + model.ContainerContentTypeDefinition = context.TypePartDefinition.ContentTypeDefinition; + }).Location("DetailAdmin", "Content:1.5"); + } - private ShapeResult InitializeDisplayListPartDisplayShape(ListPart listPart, BuildPartDisplayContext context) + private ShapeResult InitializeDisplayListPartDetailAdminShape(ListPart listPart, BuildPartDisplayContext context) + { + return Initialize("ListPartDetailAdmin", (Func)(async model => { - return Initialize(GetDisplayShapeType(context), async model => - { - var pager = await GetPagerSlimAsync(context); - var settings = context.TypePartDefinition.GetSettings(); - var containedItemOptions = new ContainedItemOptions(); - model.ContentItems = (await _containerService.QueryContainedItemsAsync( - listPart.ContentItem.ContentItemId, - settings.EnableOrdering, - pager, - containedItemOptions)).ToArray(); - - model.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(settings); - model.Context = context; - model.Pager = await context.New.PagerSlim(pager); - model.ListPart = listPart; - }) - .Location("Detail", "Content:10"); - } + var pager = await GetPagerSlimAsync(context); + var settings = context.TypePartDefinition.GetSettings(); + var listPartFilterViewModel = new ListPartFilterViewModel(); + var containedItemOptions = new ContainedItemOptions(); + + await _updateModelAccessor.ModelUpdater.TryUpdateModelAsync(listPartFilterViewModel, Prefix); + model.ListPart = listPart; + containedItemOptions.DisplayText = listPartFilterViewModel.DisplayText; + containedItemOptions.Status = listPartFilterViewModel.Status; + model.ListPartFilterViewModel = listPartFilterViewModel; + + model.ContentItems = (await _containerService.QueryContainedItemsAsync( + listPart.ContentItem.ContentItemId, + settings.EnableOrdering, + pager, + containedItemOptions)).ToArray(); + + model.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(settings); + model.Context = context; + model.EnableOrdering = settings.EnableOrdering; + model.Pager = await context.New.PagerSlim(pager); + })) + .Location("DetailAdmin", "Content:10"); + } - private static async Task GetPagerSlimAsync(BuildPartDisplayContext context) + private ShapeResult InitializeDisplayListPartDisplayShape(ListPart listPart, BuildPartDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), async model => { + var pager = await GetPagerSlimAsync(context); var settings = context.TypePartDefinition.GetSettings(); - var pagerParameters = new PagerSlimParameters(); - await context.Updater.TryUpdateModelAsync(pagerParameters); + var containedItemOptions = new ContainedItemOptions(); + model.ContentItems = (await _containerService.QueryContainedItemsAsync( + listPart.ContentItem.ContentItemId, + settings.EnableOrdering, + pager, + containedItemOptions)).ToArray(); + + model.ContainedContentTypeDefinitions = await GetContainedContentTypesAsync(settings); + model.Context = context; + model.Pager = await context.New.PagerSlim(pager); + model.ListPart = listPart; + }) + .Location("Detail", "Content:10"); + } - var pager = new PagerSlim(pagerParameters, settings.PageSize); + private static async Task GetPagerSlimAsync(BuildPartDisplayContext context) + { + var settings = context.TypePartDefinition.GetSettings(); + var pagerParameters = new PagerSlimParameters(); + await context.Updater.TryUpdateModelAsync(pagerParameters); - return pager; - } + var pager = new PagerSlim(pagerParameters, settings.PageSize); - private async Task> GetContainedContentTypesAsync(ListPartSettings settings) - { - var contentTypes = settings.ContainedContentTypes ?? []; + return pager; + } - var definitions = new List(); + private async Task> GetContainedContentTypesAsync(ListPartSettings settings) + { + var contentTypes = settings.ContainedContentTypes ?? []; - foreach (var contentType in contentTypes) - { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); + var definitions = new List(); - if (definition == null) - { - continue; - } + foreach (var contentType in contentTypes) + { + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); - definitions.Add(definition); + if (definition == null) + { + continue; } - return definitions; + definitions.Add(definition); } + + return definitions; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedEditViewModel.cs index 9276894a64c..0ae4aecd6e7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedEditViewModel.cs @@ -1,15 +1,14 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Lists.Feeds +namespace OrchardCore.Lists.Feeds; + +public class ListFeedEditViewModel { - public class ListFeedEditViewModel - { - public bool DisableRssFeed { get; set; } + public bool DisableRssFeed { get; set; } - public string FeedProxyUrl { get; set; } + public string FeedProxyUrl { get; set; } - public int FeedItemsCount { get; set; } = ListFeedQuery.DefaultItemsCount; + public int FeedItemsCount { get; set; } = ListFeedQuery.DefaultItemsCount; - public ContentItem ContentItem { get; set; } - } + public ContentItem ContentItem { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQuery.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQuery.cs index 992c05abb13..85f17ed3b8a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQuery.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQuery.cs @@ -11,108 +11,107 @@ using OrchardCore.Lists.Indexes; using YesSql; -namespace OrchardCore.Lists.Feeds +namespace OrchardCore.Lists.Feeds; + +public class ListFeedQuery : IFeedQueryProvider, IFeedQuery { - public class ListFeedQuery : IFeedQueryProvider, IFeedQuery + public const int DefaultItemsCount = 20; + + private readonly IContentManager _contentManager; + private readonly ISession _session; + + public ListFeedQuery(IContentManager contentManager, ISession session) { - public const int DefaultItemsCount = 20; + _contentManager = contentManager; + _session = session; + } - private readonly IContentManager _contentManager; - private readonly ISession _session; + public async Task MatchAsync(FeedContext context) + { + var model = new ListFeedQueryViewModel(); - public ListFeedQuery(IContentManager contentManager, ISession session) + if (!await context.Updater.TryUpdateModelAsync(model) || model.ContentItemId == null) { - _contentManager = contentManager; - _session = session; + return null; } - public async Task MatchAsync(FeedContext context) - { - var model = new ListFeedQueryViewModel(); + var contentItem = await _contentManager.GetAsync(model.ContentItemId); + var feedMetadata = await _contentManager.PopulateAspectAsync(contentItem); - if (!await context.Updater.TryUpdateModelAsync(model) || model.ContentItemId == null) - { - return null; - } + if (feedMetadata.DisableRssFeed) + { + return null; + } - var contentItem = await _contentManager.GetAsync(model.ContentItemId); - var feedMetadata = await _contentManager.PopulateAspectAsync(contentItem); + return new FeedQueryMatch { FeedQuery = this, Priority = -5 }; + } - if (feedMetadata.DisableRssFeed) - { - return null; - } + public async Task ExecuteAsync(FeedContext context) + { + var model = new ListFeedQueryViewModel(); - return new FeedQueryMatch { FeedQuery = this, Priority = -5 }; + if (!await context.Updater.TryUpdateModelAsync(model) || model.ContentItemId == null) + { + return; } - public async Task ExecuteAsync(FeedContext context) - { - var model = new ListFeedQueryViewModel(); + var contentItem = await _contentManager.GetAsync(model.ContentItemId); - if (!await context.Updater.TryUpdateModelAsync(model) || model.ContentItemId == null) - { - return; - } + if (contentItem == null) + { + return; + } - var contentItem = await _contentManager.GetAsync(model.ContentItemId); + var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); + var bodyAspect = await _contentManager.PopulateAspectAsync(contentItem); + var routes = contentItemMetadata.DisplayRouteValues; - if (contentItem == null) - { - return; - } + if (context.Format == "rss") + { + var link = new XElement("link"); + context.Response.Element.SetElementValue("title", contentItem.DisplayText); + context.Response.Element.Add(link); - var contentItemMetadata = await _contentManager.PopulateAspectAsync(contentItem); - var bodyAspect = await _contentManager.PopulateAspectAsync(contentItem); - var routes = contentItemMetadata.DisplayRouteValues; + context.Response.Element.Add(new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - if (context.Format == "rss") + context.Response.Contextualize(contextualize => { - var link = new XElement("link"); - context.Response.Element.SetElementValue("title", contentItem.DisplayText); - context.Response.Element.Add(link); - - context.Response.Element.Add(new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); + var request = contextualize.Url.ActionContext.HttpContext.Request; + var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); - context.Response.Contextualize(contextualize => - { - var request = contextualize.Url.ActionContext.HttpContext.Request; - var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); + link.Add(url); + }); + } + else + { + context.Builder.AddProperty(context, null, "title", contentItem.DisplayText); + context.Builder.AddProperty(context, null, new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - link.Add(url); - }); - } - else + context.Response.Contextualize(contextualize => { - context.Builder.AddProperty(context, null, "title", contentItem.DisplayText); - context.Builder.AddProperty(context, null, new XElement("description", new XCData(bodyAspect.Body?.ToString() ?? string.Empty))); - - context.Response.Contextualize(contextualize => - { - var request = contextualize.Url.ActionContext.HttpContext.Request; - var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); + var request = contextualize.Url.ActionContext.HttpContext.Request; + var url = contextualize.Url.Action(routes["action"].ToString(), routes["controller"].ToString(), routes, request.Scheme); - context.Builder.AddProperty(context, null, "link", url); - }); - } + context.Builder.AddProperty(context, null, "link", url); + }); + } - int itemsCount = contentItem.Content.ListPart?.FeedItemsCount ?? DefaultItemsCount; + int itemsCount = contentItem.Content.ListPart?.FeedItemsCount ?? DefaultItemsCount; - IEnumerable items; + IEnumerable items; - items = itemsCount == 0 - ? Enumerable.Empty() - : await _session.Query() - .With(x => x.ListContentItemId == contentItem.ContentItemId) - .With(x => x.Published) - .OrderByDescending(x => x.CreatedUtc) - .Take(itemsCount) - .ListAsync(); + items = itemsCount == 0 + ? Enumerable.Empty() + : await _session.Query() + .With(x => x.ListContentItemId == contentItem.ContentItemId) + .With(x => x.Published) + .OrderByDescending(x => x.CreatedUtc) + .Take(itemsCount) + .ListAsync(); - foreach (var item in items) - { - context.Builder.AddItem(context, item); - } + foreach (var item in items) + { + context.Builder.AddItem(context, item); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQueryViewModel.cs index b296ea41c44..1c8b4d2894f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListFeedQueryViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Lists.Feeds +namespace OrchardCore.Lists.Feeds; + +public class ListFeedQueryViewModel { - public class ListFeedQueryViewModel - { - public string ContentItemId { get; set; } - } + public string ContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedDisplayDriver.cs index 94bc71f510b..0e2b5a03d40 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedDisplayDriver.cs @@ -5,44 +5,43 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.Feeds +namespace OrchardCore.Lists.Feeds; + +public sealed class ListPartFeedDisplayDriver : ContentPartDisplayDriver { - public sealed class ListPartFeedDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) { - public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) + return Dynamic("ListPartFeed", shape => { - return Dynamic("ListPartFeed", shape => - { - shape.ContentItem = listPart.ContentItem; - }) - .Location("Detail", "Content"); - } + shape.ContentItem = listPart.ContentItem; + }) + .Location("Detail", "Content"); + } - public override IDisplayResult Edit(ListPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(ListPart part, BuildPartEditorContext context) + { + return Initialize("ListPartFeed_Edit", m => { - return Initialize("ListPartFeed_Edit", m => - { - m.DisableRssFeed = part.ContentItem.Content.ListPart.DisableRssFeed ?? false; - m.FeedProxyUrl = part.ContentItem.Content.ListPart.FeedProxyUrl; - m.FeedItemsCount = part.ContentItem.Content.ListPart.FeedItemsCount ?? 20; - m.ContentItem = part.ContentItem; - }); - } + m.DisableRssFeed = part.ContentItem.Content.ListPart.DisableRssFeed ?? false; + m.FeedProxyUrl = part.ContentItem.Content.ListPart.FeedProxyUrl; + m.FeedItemsCount = part.ContentItem.Content.ListPart.FeedItemsCount ?? 20; + m.ContentItem = part.ContentItem; + }); + } - public override async Task UpdateAsync(ListPart part, UpdatePartEditorContext context) + public override async Task UpdateAsync(ListPart part, UpdatePartEditorContext context) + { + var model = new ListFeedEditViewModel { - var model = new ListFeedEditViewModel - { - ContentItem = part.ContentItem, - }; + ContentItem = part.ContentItem, + }; - await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.DisableRssFeed, t => t.FeedProxyUrl, t => t.FeedItemsCount); + await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.DisableRssFeed, t => t.FeedProxyUrl, t => t.FeedItemsCount); - part.ContentItem.Content.ListPart.DisableRssFeed = model.DisableRssFeed; - part.ContentItem.Content.ListPart.FeedProxyUrl = model.FeedProxyUrl; - part.ContentItem.Content.ListPart.FeedItemsCount = model.FeedItemsCount; + part.ContentItem.Content.ListPart.DisableRssFeed = model.DisableRssFeed; + part.ContentItem.Content.ListPart.FeedProxyUrl = model.FeedProxyUrl; + part.ContentItem.Content.ListPart.FeedItemsCount = model.FeedItemsCount; - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedHandler.cs index 6a007e2206d..5aee46a02c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Feeds/ListPartFeedHandler.cs @@ -3,20 +3,19 @@ using OrchardCore.Feeds.Models; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public class ListPartFeedHandler : ContentPartHandler { - public class ListPartFeedHandler : ContentPartHandler + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, ListPart part) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, ListPart part) + return context.ForAsync(feedMetadata => { - return context.ForAsync(feedMetadata => - { - // Enable the feed if the value is not defined. This is handy to migrate the old feeds - feedMetadata.DisableRssFeed = part.Content.DisableRssFeed ?? false; - feedMetadata.FeedProxyUrl = part.Content.FeedProxyUrl; + // Enable the feed if the value is not defined. This is handy to migrate the old feeds + feedMetadata.DisableRssFeed = part.Content.DisableRssFeed ?? false; + feedMetadata.FeedProxyUrl = part.Content.FeedProxyUrl; - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs index a4646c1da10..4f242ab09c7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedInputObjectType.cs @@ -3,16 +3,15 @@ using OrchardCore.Apis.GraphQL.Queries; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.GraphQL +namespace OrchardCore.Lists.GraphQL; + +public class ContainedInputObjectType : WhereInputObjectGraphType { - public class ContainedInputObjectType : WhereInputObjectGraphType + public ContainedInputObjectType(IStringLocalizer S) { - public ContainedInputObjectType(IStringLocalizer S) - { - Name = "ContainedPartInput"; - Description = S["the list part of the content item"]; + Name = "ContainedPartInput"; + Description = S["the list part of the content item"]; - AddScalarFilterFields("listContentItemId", S["the content item id of the parent list of the content item to filter"]); - } + AddScalarFilterFields("listContentItemId", S["the content item id of the parent list of the content item to filter"]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedPartIndexAliasProvider.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedPartIndexAliasProvider.cs index 0640a59e123..9f3b4a12e10 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedPartIndexAliasProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedPartIndexAliasProvider.cs @@ -3,23 +3,22 @@ using OrchardCore.ContentManagement.GraphQL.Queries; using OrchardCore.Lists.Indexes; -namespace OrchardCore.Lists.GraphQL -{ - public class ContainedPartIndexAliasProvider : IIndexAliasProvider - { - private static readonly IndexAlias[] _aliases = - [ - new IndexAlias - { - Alias = "containedPart", - Index = nameof(ContainedPartIndex), - IndexType = typeof(ContainedPartIndex) - } - ]; +namespace OrchardCore.Lists.GraphQL; - public ValueTask> GetAliasesAsync() +public class ContainedPartIndexAliasProvider : IIndexAliasProvider +{ + private static readonly IndexAlias[] _aliases = + [ + new IndexAlias { - return ValueTask.FromResult>(_aliases); + Alias = "containedPart", + Index = nameof(ContainedPartIndex), + IndexType = typeof(ContainedPartIndex) } + ]; + + public ValueTask> GetAliasesAsync() + { + return ValueTask.FromResult>(_aliases); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedQueryObjectType.cs index 9e647376950..bc97c784c6c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ContainedQueryObjectType.cs @@ -2,17 +2,16 @@ using Microsoft.Extensions.Localization; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.GraphQL +namespace OrchardCore.Lists.GraphQL; + +public class ContainedQueryObjectType : ObjectGraphType { - public class ContainedQueryObjectType : ObjectGraphType + public ContainedQueryObjectType(IStringLocalizer S) { - public ContainedQueryObjectType(IStringLocalizer S) - { - Name = "ContainedPart"; - Description = S["Represents a link to the parent content item, and the order that content item is represented."]; + Name = "ContainedPart"; + Description = S["Represents a link to the parent content item, and the order that content item is represented."]; - Field(x => x.ListContentItemId); - Field(x => x.Order); - } + Field(x => x.ListContentItemId); + Field(x => x.Order); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs index 0862a60aa53..3b4854ebae5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/ListQueryObjectType.cs @@ -15,49 +15,48 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Lists.GraphQL +namespace OrchardCore.Lists.GraphQL; + +public class ListQueryObjectType : ObjectGraphType { - public class ListQueryObjectType : ObjectGraphType + public ListQueryObjectType(IStringLocalizer S) { - public ListQueryObjectType(IStringLocalizer S) - { - Name = "ListPart"; - Description = S["Represents a collection of content items."]; - - Field, IEnumerable>("contentItems") - .Description("the content items") - .Argument("first", "the first n elements (10 by default)", config => config.DefaultValue = 10) - .Argument("skip", "the number of elements to skip", config => config.DefaultValue = 0) - // Important to use ResolveLockedAsync to prevent concurrency error on database query, when using nested content items with List part - .ResolveLockedAsync(async g => - { - var serviceProvider = g.RequestServices; - var session = serviceProvider.GetService(); - var accessor = serviceProvider.GetRequiredService(); - - var dataLoader = accessor.Context.GetOrAddCollectionBatchLoader("ContainedPublishedContentItems", - x => LoadPublishedContentItemsForListAsync(x, session, g.GetArgument("skip"), g.GetArgument("first"))); - - return await dataLoader.LoadAsync(g.Source.ContentItem.ContentItemId).GetResultAsync(); - }); - } + Name = "ListPart"; + Description = S["Represents a collection of content items."]; - private static async Task> LoadPublishedContentItemsForListAsync(IEnumerable contentItemIds, ISession session, int skip, int count) - { - if (contentItemIds is null || !contentItemIds.Any()) + Field, IEnumerable>("contentItems") + .Description("the content items") + .Argument("first", "the first n elements (10 by default)", config => config.DefaultValue = 10) + .Argument("skip", "the number of elements to skip", config => config.DefaultValue = 0) + // Important to use ResolveLockedAsync to prevent concurrency error on database query, when using nested content items with List part + .ResolveLockedAsync(async g => { - return new Dictionary().ToLookup(k => k.Key, v => v.Value); - } - - var query = await session.Query() - .With(ci => ci.Published) - .With(cp => cp.ListContentItemId.IsIn(contentItemIds)) - .OrderBy(o => o.Order) - .Skip(skip) - .Take(count) - .ListAsync(); - - return query.ToLookup(k => k.As().ListContentItemId); + var serviceProvider = g.RequestServices; + var session = serviceProvider.GetService(); + var accessor = serviceProvider.GetRequiredService(); + + var dataLoader = accessor.Context.GetOrAddCollectionBatchLoader("ContainedPublishedContentItems", + x => LoadPublishedContentItemsForListAsync(x, session, g.GetArgument("skip"), g.GetArgument("first"))); + + return await dataLoader.LoadAsync(g.Source.ContentItem.ContentItemId).GetResultAsync(); + }); + } + + private static async Task> LoadPublishedContentItemsForListAsync(IEnumerable contentItemIds, ISession session, int skip, int count) + { + if (contentItemIds is null || !contentItemIds.Any()) + { + return new Dictionary().ToLookup(k => k.Key, v => v.Value); } + + var query = await session.Query() + .With(ci => ci.Published) + .With(cp => cp.ListContentItemId.IsIn(contentItemIds)) + .OrderBy(o => o.Order) + .Skip(skip) + .Take(count) + .ListAsync(); + + return query.ToLookup(k => k.As().ListContentItemId); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/Startup.cs index c8ab6761053..2571117f7a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/GraphQL/Startup.cs @@ -6,18 +6,17 @@ using OrchardCore.Lists.Models; using OrchardCore.Modules; -namespace OrchardCore.Lists.GraphQL +namespace OrchardCore.Lists.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddInputObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddTransient(); - services.AddWhereInputIndexPropertyProvider(); - } + services.AddInputObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddTransient(); + services.AddWhereInputIndexPropertyProvider(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartHandler.cs index f91fc6dfe89..017748fecdd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartHandler.cs @@ -8,38 +8,37 @@ using OrchardCore.Lists.Models; using OrchardCore.Lists.Services; -namespace OrchardCore.Lists.Handlers +namespace OrchardCore.Lists.Handlers; + +public class ContainedPartHandler : ContentHandlerBase { - public class ContainedPartHandler : ContentHandlerBase + private readonly IServiceProvider _serviceProvider; + + public ContainedPartHandler(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public ContainedPartHandler(IServiceProvider serviceProvider) + public override async Task CloningAsync(CloneContentContext context) + { + var containedPart = context.CloneContentItem.As(); + if (containedPart != null) { - _serviceProvider = serviceProvider; - } + // Resolve from DI to avoid circular references. + var contentManager = _serviceProvider.GetRequiredService(); - public override async Task CloningAsync(CloneContentContext context) - { - var containedPart = context.CloneContentItem.As(); - if (containedPart != null) + var listContentItem = await contentManager.GetAsync(containedPart.ListContentItemId); + if (listContentItem != null) { - // Resolve from DI to avoid circular references. - var contentManager = _serviceProvider.GetRequiredService(); - - var listContentItem = await contentManager.GetAsync(containedPart.ListContentItemId); - if (listContentItem != null) + var contentDefinitionManager = _serviceProvider.GetRequiredService(); + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(listContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "ListPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); + if (settings.EnableOrdering) { - var contentDefinitionManager = _serviceProvider.GetRequiredService(); - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(listContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "ListPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); - if (settings.EnableOrdering) - { - var containerService = _serviceProvider.GetRequiredService(); - var nextOrder = await containerService.GetNextOrderNumberAsync(containedPart.ListContentItemId); - context.CloneContentItem.Alter(x => x.Order = nextOrder); - } + var containerService = _serviceProvider.GetRequiredService(); + var nextOrder = await containerService.GetNextOrderNumberAsync(containedPart.ListContentItemId); + context.CloneContentItem.Alter(x => x.Order = nextOrder); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartLocalizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartLocalizationHandler.cs index 3db3939450d..e0624157854 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartLocalizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ContainedPartLocalizationHandler.cs @@ -7,63 +7,62 @@ using OrchardCore.Lists.Models; using YesSql; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public class ContainedPartLocalizationHandler : ContentLocalizationPartHandlerBase { - public class ContainedPartLocalizationHandler : ContentLocalizationPartHandlerBase + private readonly ISession _session; + + public ContainedPartLocalizationHandler(ISession session) { - private readonly ISession _session; + _session = session; + } - public ContainedPartLocalizationHandler(ISession session) + /// + /// Assign the Localized version of the List when localizing the Contained Item. + /// + public override async Task LocalizingAsync(LocalizationContentContext context, LocalizationPart part) + { + var containedPart = context.ContentItem.As(); + // todo: remove this check and change the handler to target ContainedPart when issue 3890 is fixed + if (containedPart != null) { - _session = session; - } + var list = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId == containedPart.ListContentItemId).FirstOrDefaultAsync(); + var localizedList = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet == list.LocalizationSet && i.Culture == context.Culture).FirstOrDefaultAsync(); - /// - /// Assign the Localized version of the List when localizing the Contained Item. - /// - public override async Task LocalizingAsync(LocalizationContentContext context, LocalizationPart part) - { - var containedPart = context.ContentItem.As(); - // todo: remove this check and change the handler to target ContainedPart when issue 3890 is fixed - if (containedPart != null) + if (localizedList != null) { - var list = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId == containedPart.ListContentItemId).FirstOrDefaultAsync(); - var localizedList = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet == list.LocalizationSet && i.Culture == context.Culture).FirstOrDefaultAsync(); - - if (localizedList != null) - { - containedPart.ListContentItemId = localizedList.ContentItemId; - containedPart.Apply(); - } + containedPart.ListContentItemId = localizedList.ContentItemId; + containedPart.Apply(); } } } +} - public class LocalizationContainedPartHandler : ContentPartHandler +public class LocalizationContainedPartHandler : ContentPartHandler +{ + private readonly ISession _session; + public LocalizationContainedPartHandler(ISession session) { - private readonly ISession _session; - public LocalizationContainedPartHandler(ISession session) - { - _session = session; - } + _session = session; + } - /// - /// Need to override CreatingAsync to set the right parent on Creation. - /// This will attach the item to the right list when the item is created from a list of another culture. - /// - public override async Task CreatingAsync(CreateContentContext context, LocalizationPart instance) + /// + /// Need to override CreatingAsync to set the right parent on Creation. + /// This will attach the item to the right list when the item is created from a list of another culture. + /// + public override async Task CreatingAsync(CreateContentContext context, LocalizationPart instance) + { + var containedPart = context.ContentItem.As(); + if (containedPart != null) { - var containedPart = context.ContentItem.As(); - if (containedPart != null) - { - var list = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId == containedPart.ListContentItemId).FirstOrDefaultAsync(); - var localizedList = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet == list.LocalizationSet && i.Culture == instance.Culture).FirstOrDefaultAsync(); + var list = await _session.QueryIndex(i => (i.Published || i.Latest) && i.ContentItemId == containedPart.ListContentItemId).FirstOrDefaultAsync(); + var localizedList = await _session.QueryIndex(i => (i.Published || i.Latest) && i.LocalizationSet == list.LocalizationSet && i.Culture == instance.Culture).FirstOrDefaultAsync(); - if (localizedList != null) - { - containedPart.ListContentItemId = localizedList.ContentItemId; - containedPart.Apply(); - } + if (localizedList != null) + { + containedPart.ListContentItemId = localizedList.ContentItemId; + containedPart.Apply(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartHandler.cs index 9b56cf435e3..846ef3115e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartHandler.cs @@ -4,24 +4,23 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public class ListPartHandler : ContentPartHandler { - public class ListPartHandler : ContentPartHandler + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, ListPart part) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, ListPart part) + return context.ForAsync(contentItemMetadata => { - return context.ForAsync(contentItemMetadata => + contentItemMetadata.AdminRouteValues = new RouteValueDictionary { - contentItemMetadata.AdminRouteValues = new RouteValueDictionary - { - {"Area", "OrchardCore.Contents"}, - {"Controller", "Admin"}, - {"Action", "Display"}, - {"ContentItemId", context.ContentItem.ContentItemId} - }; + {"Area", "OrchardCore.Contents"}, + {"Controller", "Admin"}, + {"Action", "Display"}, + {"ContentItemId", context.ContentItem.ContentItemId} + }; - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartLocalizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartLocalizationHandler.cs index ae01e19204f..4539482d572 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartLocalizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Handlers/ListPartLocalizationHandler.cs @@ -7,41 +7,40 @@ using OrchardCore.Lists.Models; using YesSql; -namespace OrchardCore.Lists.Drivers +namespace OrchardCore.Lists.Drivers; + +public class ListPartLocalizationHandler : ContentLocalizationPartHandlerBase { - public class ListPartLocalizationHandler : ContentLocalizationPartHandlerBase + private readonly ISession _session; + + public ListPartLocalizationHandler(ISession session) + { + _session = session; + } + + /// + /// Select Contained ContentItems that are already in the target culture + /// but attached to the original list and reassign their ListContenItemId. + /// + public override async Task LocalizedAsync(LocalizationContentContext context, ListPart part) { - private readonly ISession _session; + var containedList = await _session.Query( + x => x.ListContentItemId == context.Original.ContentItemId).ListAsync(); - public ListPartLocalizationHandler(ISession session) + if (!containedList.Any()) { - _session = session; + return; } - /// - /// Select Contained ContentItems that are already in the target culture - /// but attached to the original list and reassign their ListContenItemId. - /// - public override async Task LocalizedAsync(LocalizationContentContext context, ListPart part) + foreach (var item in containedList) { - var containedList = await _session.Query( - x => x.ListContentItemId == context.Original.ContentItemId).ListAsync(); - - if (!containedList.Any()) - { - return; - } - - foreach (var item in containedList) + var localizationPart = item.As(); + if (localizationPart.Culture == context.Culture) { - var localizationPart = item.As(); - if (localizationPart.Culture == context.Culture) - { - var cp = item.As(); - cp.ListContentItemId = context.ContentItem.ContentItemId; - cp.Apply(); - await _session.SaveAsync(item); - } + var cp = item.As(); + cp.ListContentItemId = context.ContentItem.ContentItemId; + cp.Apply(); + await _session.SaveAsync(item); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Helpers/ListQueryHelpers.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Helpers/ListQueryHelpers.cs index 3e536212370..f5698e5c9b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Helpers/ListQueryHelpers.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Helpers/ListQueryHelpers.cs @@ -7,24 +7,23 @@ using OrchardCore.Lists.Indexes; using YesSql; -namespace OrchardCore.Lists.Helpers +namespace OrchardCore.Lists.Helpers; + +internal static class ListQueryHelpers { - internal static class ListQueryHelpers + internal static Task QueryListItemsCountAsync(ISession session, string listContentItemId, Expression> itemPredicate = null) { - internal static Task QueryListItemsCountAsync(ISession session, string listContentItemId, Expression> itemPredicate = null) - { - return session.Query() - .With(x => x.ListContentItemId == listContentItemId) - .With(itemPredicate ?? (x => x.Published)) - .CountAsync(); - } + return session.Query() + .With(x => x.ListContentItemId == listContentItemId) + .With(itemPredicate ?? (x => x.Published)) + .CountAsync(); + } - internal static Task> QueryListItemsAsync(ISession session, string listContentItemId, Expression> itemPredicate = null) - { - return session.Query() - .With(x => x.ListContentItemId == listContentItemId) - .With(itemPredicate ?? (x => x.Published)) - .ListAsync(); - } + internal static Task> QueryListItemsAsync(ISession session, string listContentItemId, Expression> itemPredicate = null) + { + return session.Query() + .With(x => x.ListContentItemId == listContentItemId) + .With(itemPredicate ?? (x => x.Published)) + .ListAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartContentIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartContentIndexHandler.cs index 50d0e3b01ba..9d2bf2ef504 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartContentIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartContentIndexHandler.cs @@ -4,30 +4,29 @@ using OrchardCore.Indexing; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.Indexes +namespace OrchardCore.Lists.Indexes; + +public class ContainedPartContentIndexHandler : IContentItemIndexHandler { - public class ContainedPartContentIndexHandler : IContentItemIndexHandler + public Task BuildIndexAsync(BuildIndexContext context) { - public Task BuildIndexAsync(BuildIndexContext context) - { - var parent = context.ContentItem.As(); + var parent = context.ContentItem.As(); - if (parent == null) - { - return Task.CompletedTask; - } + if (parent == null) + { + return Task.CompletedTask; + } - context.DocumentIndex.Set( - IndexingConstants.ContainedPartKey + IndexingConstants.IdsKey, - parent.ListContentItemId, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + context.DocumentIndex.Set( + IndexingConstants.ContainedPartKey + IndexingConstants.IdsKey, + parent.ListContentItemId, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - context.DocumentIndex.Set( - IndexingConstants.ContainedPartKey + IndexingConstants.OrderKey, - parent.Order, - DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); + context.DocumentIndex.Set( + IndexingConstants.ContainedPartKey + IndexingConstants.OrderKey, + parent.Order, + DocumentIndexOptions.Keyword | DocumentIndexOptions.Store); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartIndex.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartIndex.cs index a48a95b8237..2d77b5f3103 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Indexes/ContainedPartIndex.cs @@ -2,55 +2,54 @@ using OrchardCore.Lists.Models; using YesSql.Indexes; -namespace OrchardCore.Lists.Indexes +namespace OrchardCore.Lists.Indexes; + +public class ContainedPartIndex : MapIndex { - public class ContainedPartIndex : MapIndex - { - public string ListContentItemId { get; set; } + public string ListContentItemId { get; set; } - public int Order { get; set; } + public int Order { get; set; } - public string ContentItemId { get; set; } + public string ContentItemId { get; set; } - public string ListContentType { get; set; } + public string ListContentType { get; set; } - public bool Published { get; set; } + public bool Published { get; set; } - public bool Latest { get; set; } + public bool Latest { get; set; } - public string DisplayText { get; set; } - } + public string DisplayText { get; set; } +} - public class ContainedPartIndexProvider : IndexProvider +public class ContainedPartIndexProvider : IndexProvider +{ + public override void Describe(DescribeContext context) { - public override void Describe(DescribeContext context) - { - context.For() - .Map(contentItem => + context.For() + .Map(contentItem => + { + if (!contentItem.Latest && !contentItem.Published) + { + return null; + } + + var containedPart = contentItem.As(); + + if (containedPart == null) + { + return null; + } + + return new ContainedPartIndex { - if (!contentItem.Latest && !contentItem.Published) - { - return null; - } - - var containedPart = contentItem.As(); - - if (containedPart == null) - { - return null; - } - - return new ContainedPartIndex - { - ContentItemId = contentItem.ContentItemId, - ListContentType = containedPart.ListContentType, - ListContentItemId = containedPart.ListContentItemId, - Order = containedPart.Order, - Published = contentItem.Published, - Latest = contentItem.Latest, - DisplayText = contentItem.DisplayText, - }; - }); - } + ContentItemId = contentItem.ContentItemId, + ListContentType = containedPart.ListContentType, + ListContentItemId = containedPart.ListContentItemId, + Order = containedPart.Order, + Published = contentItem.Published, + Latest = contentItem.Latest, + DisplayText = contentItem.DisplayText, + }; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ContainerFilter.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ContainerFilter.cs index b980e7cecfc..a67b209bd27 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ContainerFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ContainerFilter.cs @@ -6,34 +6,33 @@ using OrchardCore.Liquid; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.Liquid +namespace OrchardCore.Lists.Liquid; + +public class ContainerFilter : ILiquidFilter { - public class ContainerFilter : ILiquidFilter + private readonly IContentManager _contentManager; + + public ContainerFilter(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public ContainerFilter(IContentManager contentManager) - { - _contentManager = contentManager; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + var contentItem = input.ToObjectValue() as ContentItem ?? throw new ArgumentException("A Content Item was expected"); - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) - { - var contentItem = input.ToObjectValue() as ContentItem ?? throw new ArgumentException("A Content Item was expected"); + var containerId = contentItem.As()?.ListContentItemId; - var containerId = contentItem.As()?.ListContentItemId; + if (containerId != null) + { + var container = await _contentManager.GetAsync(containerId); - if (containerId != null) + if (container != null) { - var container = await _contentManager.GetAsync(containerId); - - if (container != null) - { - return new ObjectValue(container); - } + return new ObjectValue(container); } - - return new ObjectValue(contentItem); } + + return new ObjectValue(contentItem); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListCountFilter.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListCountFilter.cs index 0d8f7549447..db54c12d5e6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListCountFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListCountFilter.cs @@ -6,33 +6,32 @@ using OrchardCore.Lists.Helpers; using YesSql; -namespace OrchardCore.Lists.Liquid +namespace OrchardCore.Lists.Liquid; + +public class ListCountFilter : ILiquidFilter { - public class ListCountFilter : ILiquidFilter + private readonly ISession _session; + + public ListCountFilter(ISession session) + { + _session = session; + } + + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) { - private readonly ISession _session; + string listContentItemId; - public ListCountFilter(ISession session) + if (input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem contentItem) { - _session = session; + listContentItemId = contentItem.ContentItemId; } - - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + else { - string listContentItemId; - - if (input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem contentItem) - { - listContentItemId = contentItem.ContentItemId; - } - else - { - listContentItemId = input.ToStringValue(); - } + listContentItemId = input.ToStringValue(); + } - var listCount = await ListQueryHelpers.QueryListItemsCountAsync(_session, listContentItemId); + var listCount = await ListQueryHelpers.QueryListItemsCountAsync(_session, listContentItemId); - return NumberValue.Create(listCount); - } + return NumberValue.Create(listCount); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListItemsFilter.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListItemsFilter.cs index b70c27d66d4..ad2794480d5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListItemsFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Liquid/ListItemsFilter.cs @@ -6,33 +6,32 @@ using OrchardCore.Lists.Helpers; using YesSql; -namespace OrchardCore.Lists.Liquid +namespace OrchardCore.Lists.Liquid; + +public class ListItemsFilter : ILiquidFilter { - public class ListItemsFilter : ILiquidFilter + private readonly ISession _session; + + public ListItemsFilter(ISession session) + { + _session = session; + } + + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) { - private readonly ISession _session; + string listContentItemId; - public ListItemsFilter(ISession session) + if (input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem contentItem) { - _session = session; + listContentItemId = contentItem.ContentItemId; } - - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + else { - string listContentItemId; - - if (input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem contentItem) - { - listContentItemId = contentItem.ContentItemId; - } - else - { - listContentItemId = input.ToStringValue(); - } + listContentItemId = input.ToStringValue(); + } - var listItems = await ListQueryHelpers.QueryListItemsAsync(_session, listContentItemId); + var listItems = await ListQueryHelpers.QueryListItemsAsync(_session, listContentItemId); - return FluidValue.Create(listItems, ctx.Options); - } + return FluidValue.Create(listItems, ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Migrations.cs index 79e6328c003..0af7d87da49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Migrations.cs @@ -6,113 +6,112 @@ using OrchardCore.Lists.Models; using YesSql.Sql; -namespace OrchardCore.Lists +namespace OrchardCore.Lists; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("ListPart", builder => builder + .Attachable() + .WithDescription("Add a list behavior.")); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("ContentItemId", column => column.WithLength(26)) + .Column("ListContentItemId", column => column.WithLength(26)) + .Column("DisplayText") + .Column("Order") + .Column("ListContentType") + .Column("Published") + .Column("Latest") + + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContainedPartIndex_DocumentId", + "Id", + "DocumentId", + "ContentItemId", + "ListContentItemId", + "DisplayText", + "Order", + "ListContentType", + "Published", + "Latest") + ); + + // Shortcut other migration steps on new content definition schemas. + return 4; + } + + // Migrate PartSettings. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _contentDefinitionManager.MigratePartSettingsAsync(); + + return 2; + } + + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContainedPartIndex_DocumentId", + "DocumentId", + "ListContentItemId", + "Order") + ); + + return 3; + } + + // This code can be removed in a later version. + public async Task UpdateFrom3Async() { - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } - - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("ListPart", builder => builder - .Attachable() - .WithDescription("Add a list behavior.")); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("ContentItemId", column => column.WithLength(26)) - .Column("ListContentItemId", column => column.WithLength(26)) - .Column("DisplayText") - .Column("Order") - .Column("ListContentType") - .Column("Published") - .Column("Latest") - - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContainedPartIndex_DocumentId", - "Id", - "DocumentId", - "ContentItemId", - "ListContentItemId", - "DisplayText", - "Order", - "ListContentType", - "Published", - "Latest") - ); - - // Shortcut other migration steps on new content definition schemas. - return 4; - } - - // Migrate PartSettings. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.MigratePartSettingsAsync(); - - return 2; - } - - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContainedPartIndex_DocumentId", + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("ContentItemId", column => column.WithLength(26)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("ListContentType") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("DisplayText") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Published") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Latest") + ); + await SchemaBuilder.AlterIndexTableAsync(table => table + .DropIndex("IDX_ContainedPartIndex_DocumentId") + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_ContainedPartIndex_DocumentId", + "Id", "DocumentId", + "ContentItemId", "ListContentItemId", - "Order") - ); - - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom3Async() - { - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("ContentItemId", column => column.WithLength(26)) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("ListContentType") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("DisplayText") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Published") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Latest") - ); - await SchemaBuilder.AlterIndexTableAsync(table => table - .DropIndex("IDX_ContainedPartIndex_DocumentId") - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_ContainedPartIndex_DocumentId", - "Id", - "DocumentId", - "ContentItemId", - "ListContentItemId", - "DisplayText", - "Order", - "ListContentType", - "Published", - "Latest") - ); - - return 4; - } + "DisplayText", + "Order", + "ListContentType", + "Published", + "Latest") + ); + + return 4; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedItemOptions.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedItemOptions.cs index 653df99e694..52b00827ed2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedItemOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedItemOptions.cs @@ -1,16 +1,15 @@ -namespace OrchardCore.Lists.Models +namespace OrchardCore.Lists.Models; + +public enum ContentsStatus { - public enum ContentsStatus - { - Published, - Latest, - Draft, - Owner - } + Published, + Latest, + Draft, + Owner +} - public class ContainedItemOptions - { - public string DisplayText { get; set; } - public ContentsStatus Status { get; set; } = ContentsStatus.Published; - } +public class ContainedItemOptions +{ + public string DisplayText { get; set; } + public ContentsStatus Status { get; set; } = ContentsStatus.Published; } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedPart.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedPart.cs index 5a62fa2ab9e..f833d90ca30 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ContainedPart.cs @@ -1,25 +1,24 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Lists.Models +namespace OrchardCore.Lists.Models; + +/// +/// Attached to a content item instance when it's part of a list. +/// +public class ContainedPart : ContentPart { /// - /// Attached to a content item instance when it's part of a list. + /// The content item id of the list owning this content item. /// - public class ContainedPart : ContentPart - { - /// - /// The content item id of the list owning this content item. - /// - public string ListContentItemId { get; set; } + public string ListContentItemId { get; set; } - /// - /// The content type of the list owning this content item. - /// - public string ListContentType { get; set; } + /// + /// The content type of the list owning this content item. + /// + public string ListContentType { get; set; } - /// - /// The order of this content item in the list. - /// - public int Order { get; set; } - } + /// + /// The order of this content item in the list. + /// + public int Order { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPart.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPart.cs index 7dbb2de7cd7..ed23924f8b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPart.cs @@ -1,8 +1,7 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Lists.Models +namespace OrchardCore.Lists.Models; + +public class ListPart : ContentPart { - public class ListPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPartSettings.cs index d9089e2382c..8db3018521b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Models/ListPartSettings.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Lists.Models +namespace OrchardCore.Lists.Models; + +public class ListPartSettings { - public class ListPartSettings - { - public int PageSize { get; set; } = 10; - public string[] ContainedContentTypes { get; set; } - public bool EnableOrdering { get; set; } - public bool ShowHeader { get; set; } - } + public int PageSize { get; set; } = 10; + public string[] ContainedContentTypes { get; set; } + public bool EnableOrdering { get; set; } + public bool ShowHeader { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/ListMetaWeblogDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/ListMetaWeblogDriver.cs index 6c508969031..c70ac26f8e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/ListMetaWeblogDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/ListMetaWeblogDriver.cs @@ -4,17 +4,16 @@ using OrchardCore.Lists.Models; using OrchardCore.Modules; -namespace OrchardCore.Lists.RemotePublishing +namespace OrchardCore.Lists.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class ListMetaWeblogDriver : ContentPartDisplayDriver { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class ListMetaWeblogDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) { - public override IDisplayResult Display(ListPart listPart, BuildPartDisplayContext context) + return Dynamic("ListPart_RemotePublishing", shape => { - return Dynamic("ListPart_RemotePublishing", shape => - { - shape.ContentItem = listPart.ContentItem; - }).Location("Content"); - } + shape.ContentItem = listPart.ContentItem; + }).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/MetaWeblogHandler.cs b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/MetaWeblogHandler.cs index e49ea1f7480..f176d9255fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/MetaWeblogHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/MetaWeblogHandler.cs @@ -25,486 +25,485 @@ using OrchardCore.XmlRpc.Models; using YesSql; -namespace OrchardCore.Lists.RemotePublishing +namespace OrchardCore.Lists.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public class MetaWeblogHandler : IXmlRpcHandler { - [RequireFeatures("OrchardCore.RemotePublishing")] - public class MetaWeblogHandler : IXmlRpcHandler + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IAuthorizationService _authorizationService; + private readonly IMediaFileStore _mediaFileStore; + private readonly IMembershipService _membershipService; + private readonly IEnumerable _metaWeblogDrivers; + private readonly ISession _session; + protected readonly IStringLocalizer S; + + public MetaWeblogHandler( + IContentManager contentManager, + IAuthorizationService authorizationService, + IMembershipService membershipService, + ISession session, + IContentDefinitionManager contentDefinitionManager, + IMediaFileStore mediaFileStore, + IEnumerable metaWeblogDrivers, + IStringLocalizer localizer) { - private readonly IContentManager _contentManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IAuthorizationService _authorizationService; - private readonly IMediaFileStore _mediaFileStore; - private readonly IMembershipService _membershipService; - private readonly IEnumerable _metaWeblogDrivers; - private readonly ISession _session; - protected readonly IStringLocalizer S; - - public MetaWeblogHandler( - IContentManager contentManager, - IAuthorizationService authorizationService, - IMembershipService membershipService, - ISession session, - IContentDefinitionManager contentDefinitionManager, - IMediaFileStore mediaFileStore, - IEnumerable metaWeblogDrivers, - IStringLocalizer localizer) - { - _contentManager = contentManager; - _contentDefinitionManager = contentDefinitionManager; - _authorizationService = authorizationService; - _metaWeblogDrivers = metaWeblogDrivers; - _session = session; - _mediaFileStore = mediaFileStore; - _membershipService = membershipService; - S = localizer; - } - - public void SetCapabilities(XElement options) - { - const string manifestUri = "http://schemas.microsoft.com/wlw/manifest/weblog"; - - foreach (var driver in _metaWeblogDrivers) - { - driver.SetCapabilities((name, value) => { options.SetElementValue(XName.Get(name, manifestUri), value); }); - } + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + _authorizationService = authorizationService; + _metaWeblogDrivers = metaWeblogDrivers; + _session = session; + _mediaFileStore = mediaFileStore; + _membershipService = membershipService; + S = localizer; + } + + public void SetCapabilities(XElement options) + { + const string manifestUri = "http://schemas.microsoft.com/wlw/manifest/weblog"; + + foreach (var driver in _metaWeblogDrivers) + { + driver.SetCapabilities((name, value) => { options.SetElementValue(XName.Get(name, manifestUri), value); }); } + } - public async Task ProcessAsync(XmlRpcContext context) + public async Task ProcessAsync(XmlRpcContext context) + { + if (context.RpcMethodCall.MethodName == "blogger.getUsersBlogs") { - if (context.RpcMethodCall.MethodName == "blogger.getUsersBlogs") - { - var result = await MetaWeblogGetUserBlogsAsync(context, - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value)); + var result = await MetaWeblogGetUserBlogsAsync(context, + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value)); - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "metaWeblog.getRecentPosts") - { - var result = await MetaWeblogGetRecentPosts( - context, - Convert.ToString(context.RpcMethodCall.Params[0].Value), - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - Convert.ToInt32(context.RpcMethodCall.Params[3].Value), - context.Drivers); - - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "metaWeblog.getRecentPosts") + { + var result = await MetaWeblogGetRecentPosts( + context, + Convert.ToString(context.RpcMethodCall.Params[0].Value), + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + Convert.ToInt32(context.RpcMethodCall.Params[3].Value), + context.Drivers); + + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "metaWeblog.newPost") - { - var result = await MetaWeblogNewPostAsync( - Convert.ToString(context.RpcMethodCall.Params[0].Value), - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - (XRpcStruct)context.RpcMethodCall.Params[3].Value, - Convert.ToBoolean(context.RpcMethodCall.Params[4].Value), - context.Drivers); - - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "metaWeblog.newPost") + { + var result = await MetaWeblogNewPostAsync( + Convert.ToString(context.RpcMethodCall.Params[0].Value), + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + (XRpcStruct)context.RpcMethodCall.Params[3].Value, + Convert.ToBoolean(context.RpcMethodCall.Params[4].Value), + context.Drivers); + + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "metaWeblog.getPost") - { - var result = await MetaWeblogGetPostAsync( - context, - Convert.ToString(context.RpcMethodCall.Params[0].Value), - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - context.Drivers); - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "metaWeblog.getPost") + { + var result = await MetaWeblogGetPostAsync( + context, + Convert.ToString(context.RpcMethodCall.Params[0].Value), + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + context.Drivers); + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "metaWeblog.editPost") - { - var result = await MetaWeblogEditPostAsync( - Convert.ToString(context.RpcMethodCall.Params[0].Value), - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - (XRpcStruct)context.RpcMethodCall.Params[3].Value, - Convert.ToBoolean(context.RpcMethodCall.Params[4].Value), - context.Drivers); - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "metaWeblog.editPost") + { + var result = await MetaWeblogEditPostAsync( + Convert.ToString(context.RpcMethodCall.Params[0].Value), + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + (XRpcStruct)context.RpcMethodCall.Params[3].Value, + Convert.ToBoolean(context.RpcMethodCall.Params[4].Value), + context.Drivers); + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "blogger.deletePost") - { - var result = await MetaWeblogDeletePostAsync( - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - Convert.ToString(context.RpcMethodCall.Params[3].Value), - context.Drivers); - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "blogger.deletePost") + { + var result = await MetaWeblogDeletePostAsync( + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + Convert.ToString(context.RpcMethodCall.Params[3].Value), + context.Drivers); + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); + } - if (context.RpcMethodCall.MethodName == "metaWeblog.newMediaObject") - { - var result = await MetaWeblogNewMediaObjectAsync( - Convert.ToString(context.RpcMethodCall.Params[1].Value), - Convert.ToString(context.RpcMethodCall.Params[2].Value), - (XRpcStruct)context.RpcMethodCall.Params[3].Value); - context.RpcMethodResponse = new XRpcMethodResponse().Add(result); - } + if (context.RpcMethodCall.MethodName == "metaWeblog.newMediaObject") + { + var result = await MetaWeblogNewMediaObjectAsync( + Convert.ToString(context.RpcMethodCall.Params[1].Value), + Convert.ToString(context.RpcMethodCall.Params[2].Value), + (XRpcStruct)context.RpcMethodCall.Params[3].Value); + context.RpcMethodResponse = new XRpcMethodResponse().Add(result); } + } + + private async Task MetaWeblogNewMediaObjectAsync(string userName, string password, XRpcStruct file) + { + _ = await ValidateUserAsync(userName, password); + + var name = file.Optional("name"); + var bits = file.Optional("bits"); - private async Task MetaWeblogNewMediaObjectAsync(string userName, string password, XRpcStruct file) + var directoryName = Path.GetDirectoryName(name); + var filePath = _mediaFileStore.Combine(directoryName, Path.GetFileName(name)); + Stream stream = null; + try { - _ = await ValidateUserAsync(userName, password); + stream = new MemoryStream(bits); + filePath = await _mediaFileStore.CreateFileFromStreamAsync(filePath, stream); + } + finally + { + stream?.Dispose(); + } - var name = file.Optional("name"); - var bits = file.Optional("bits"); + var publicUrl = _mediaFileStore.MapPathToPublicUrl(filePath); - var directoryName = Path.GetDirectoryName(name); - var filePath = _mediaFileStore.Combine(directoryName, Path.GetFileName(name)); - Stream stream = null; - try - { - stream = new MemoryStream(bits); - filePath = await _mediaFileStore.CreateFileFromStreamAsync(filePath, stream); - } - finally - { - stream?.Dispose(); - } + // Some clients require all optional attributes to be declared Wordpress responds in this way as well. + return new XRpcStruct() + .Set("file", publicUrl) + .Set("url", publicUrl) + .Set("type", file.Optional("type")); + } - var publicUrl = _mediaFileStore.MapPathToPublicUrl(filePath); + private async Task MetaWeblogGetUserBlogsAsync(XmlRpcContext context, string userName, string password) + { + var user = await ValidateUserAsync(userName, password); - // Some clients require all optional attributes to be declared Wordpress responds in this way as well. - return new XRpcStruct() - .Set("file", publicUrl) - .Set("url", publicUrl) - .Set("type", file.Optional("type")); - } + var array = new XRpcArray(); - private async Task MetaWeblogGetUserBlogsAsync(XmlRpcContext context, string userName, string password) + // Look for all types using ListPart. + foreach (var type in await _contentDefinitionManager.ListTypeDefinitionsAsync()) { - var user = await ValidateUserAsync(userName, password); - - var array = new XRpcArray(); + if (!type.Parts.Any(x => x.Name == nameof(ListPart))) + { + continue; + } - // Look for all types using ListPart. - foreach (var type in await _contentDefinitionManager.ListTypeDefinitionsAsync()) + foreach (var list in await _session.Query(x => x.ContentType == type.Name).ListAsync()) { - if (!type.Parts.Any(x => x.Name == nameof(ListPart))) + // User needs to at least have permission to edit its own blog posts to access the service. + if (await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, list)) { - continue; - } + var metadata = await _contentManager.PopulateAspectAsync(list); + var displayRouteValues = metadata.DisplayRouteValues; - foreach (var list in await _session.Query(x => x.ContentType == type.Name).ListAsync()) - { - // User needs to at least have permission to edit its own blog posts to access the service. - if (await _authorizationService.AuthorizeAsync(user, CommonPermissions.EditContent, list)) - { - var metadata = await _contentManager.PopulateAspectAsync(list); - var displayRouteValues = metadata.DisplayRouteValues; - - array.Add(new XRpcStruct() - .Set("url", context.Url.Action(displayRouteValues["action"].ToString(), displayRouteValues["controller"].ToString(), displayRouteValues, context.HttpContext.Request.Scheme)) - .Set("blogid", list.ContentItemId) - .Set("blogName", list.DisplayText)); - } + array.Add(new XRpcStruct() + .Set("url", context.Url.Action(displayRouteValues["action"].ToString(), displayRouteValues["controller"].ToString(), displayRouteValues, context.HttpContext.Request.Scheme)) + .Set("blogid", list.ContentItemId) + .Set("blogName", list.DisplayText)); } } - - return array; } - private async Task MetaWeblogGetRecentPosts( - XmlRpcContext context, - string contentItemId, - string userName, - string password, - int numberOfPosts, - IEnumerable drivers) - { - var user = await ValidateUserAsync(userName, password); + return array; + } - // User needs to at least have permission to edit its own blog posts to access the service. - await CheckAccessAsync(CommonPermissions.EditContent, user, null); + private async Task MetaWeblogGetRecentPosts( + XmlRpcContext context, + string contentItemId, + string userName, + string password, + int numberOfPosts, + IEnumerable drivers) + { + var user = await ValidateUserAsync(userName, password); - var list = (await _contentManager.GetAsync(contentItemId)) - ?? throw new InvalidOperationException("Could not find content item " + contentItemId); + // User needs to at least have permission to edit its own blog posts to access the service. + await CheckAccessAsync(CommonPermissions.EditContent, user, null); - var array = new XRpcArray(); + var list = (await _contentManager.GetAsync(contentItemId)) + ?? throw new InvalidOperationException("Could not find content item " + contentItemId); - var contentItems = await _session.Query() - .With(x => x.ListContentItemId == contentItemId) - .With(x => x.Latest) - .OrderByDescending(x => x.CreatedUtc) - .Take(numberOfPosts) - .ListAsync(); + var array = new XRpcArray(); - foreach (var contentItem in contentItems) - { - var postStruct = await CreateBlogStructAsync(context, contentItem); + var contentItems = await _session.Query() + .With(x => x.ListContentItemId == contentItemId) + .With(x => x.Latest) + .OrderByDescending(x => x.CreatedUtc) + .Take(numberOfPosts) + .ListAsync(); - foreach (var driver in drivers) - { - driver.Process(postStruct); - } + foreach (var contentItem in contentItems) + { + var postStruct = await CreateBlogStructAsync(context, contentItem); - array.Add(postStruct); + foreach (var driver in drivers) + { + driver.Process(postStruct); } - return array; + array.Add(postStruct); } - private async Task MetaWeblogNewPostAsync( - string contentItemId, - string userName, - string password, - XRpcStruct content, - bool publish, - IEnumerable drivers) - { - var user = await ValidateUserAsync(userName, password); - - // User needs permission to edit or publish its own blog posts. - await CheckAccessAsync(publish ? CommonPermissions.PublishContent : CommonPermissions.EditContent, user, null); + return array; + } - var list = (await _contentManager.GetAsync(contentItemId)) - ?? throw new InvalidOperationException("Could not find content item " + contentItemId); + private async Task MetaWeblogNewPostAsync( + string contentItemId, + string userName, + string password, + XRpcStruct content, + bool publish, + IEnumerable drivers) + { + var user = await ValidateUserAsync(userName, password); - var postType = (await GetContainedContentTypesAsync(list)).FirstOrDefault(); - var contentItem = await _contentManager.NewAsync(postType.Name); + // User needs permission to edit or publish its own blog posts. + await CheckAccessAsync(publish ? CommonPermissions.PublishContent : CommonPermissions.EditContent, user, null); - contentItem.Owner = user.FindFirstValue(ClaimTypes.NameIdentifier); - contentItem.Alter(x => x.ListContentItemId = list.ContentItemId); + var list = (await _contentManager.GetAsync(contentItemId)) + ?? throw new InvalidOperationException("Could not find content item " + contentItemId); - foreach (var driver in _metaWeblogDrivers) - { - driver.EditPost(content, contentItem); - } + var postType = (await GetContainedContentTypesAsync(list)).FirstOrDefault(); + var contentItem = await _contentManager.NewAsync(postType.Name); - await _contentManager.CreateAsync(contentItem, VersionOptions.Draft); + contentItem.Owner = user.FindFirstValue(ClaimTypes.NameIdentifier); + contentItem.Alter(x => x.ListContentItemId = list.ContentItemId); - // Try to get the UTC time zone by default. - var publishedUtc = content.Optional("date_created_gmt"); - if (publishedUtc == null) - { - // Take the local one - publishedUtc = content.Optional("dateCreated"); - } - else - { - // Ensure it's read as a UTC time. - publishedUtc = new DateTime(publishedUtc.Value.Ticks, DateTimeKind.Utc); - } + foreach (var driver in _metaWeblogDrivers) + { + driver.EditPost(content, contentItem); + } - if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow)) - { - await _contentManager.PublishAsync(contentItem); - } - else - { - await _contentManager.SaveDraftAsync(contentItem); - } + await _contentManager.CreateAsync(contentItem, VersionOptions.Draft); - if (publishedUtc != null) - { - contentItem.CreatedUtc = publishedUtc; - } + // Try to get the UTC time zone by default. + var publishedUtc = content.Optional("date_created_gmt"); + if (publishedUtc == null) + { + // Take the local one + publishedUtc = content.Optional("dateCreated"); + } + else + { + // Ensure it's read as a UTC time. + publishedUtc = new DateTime(publishedUtc.Value.Ticks, DateTimeKind.Utc); + } - foreach (var driver in drivers) - { - driver.Process(contentItem.ContentItemId); - } + if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow)) + { + await _contentManager.PublishAsync(contentItem); + } + else + { + await _contentManager.SaveDraftAsync(contentItem); + } - return contentItem.ContentItemId; + if (publishedUtc != null) + { + contentItem.CreatedUtc = publishedUtc; } - private async Task MetaWeblogGetPostAsync( - XmlRpcContext context, - string contentItemId, - string userName, - string password, - IEnumerable drivers) + foreach (var driver in drivers) { - var user = await ValidateUserAsync(userName, password); + driver.Process(contentItem.ContentItemId); + } - var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.Latest)) - ?? throw new InvalidOperationException("Could not find content item " + contentItemId); + return contentItem.ContentItemId; + } - await CheckAccessAsync(CommonPermissions.EditContent, user, contentItem); + private async Task MetaWeblogGetPostAsync( + XmlRpcContext context, + string contentItemId, + string userName, + string password, + IEnumerable drivers) + { + var user = await ValidateUserAsync(userName, password); - var postStruct = await CreateBlogStructAsync(context, contentItem); + var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.Latest)) + ?? throw new InvalidOperationException("Could not find content item " + contentItemId); - foreach (var driver in _metaWeblogDrivers) - { - driver.BuildPost(postStruct, context, contentItem); - } + await CheckAccessAsync(CommonPermissions.EditContent, user, contentItem); - foreach (var driver in drivers) - { - driver.Process(postStruct); - } + var postStruct = await CreateBlogStructAsync(context, contentItem); - return postStruct; + foreach (var driver in _metaWeblogDrivers) + { + driver.BuildPost(postStruct, context, contentItem); } - private async Task MetaWeblogEditPostAsync( - string contentItemId, - string userName, - string password, - XRpcStruct content, - bool publish, - IEnumerable drivers) + foreach (var driver in drivers) { - var user = await ValidateUserAsync(userName, password); + driver.Process(postStruct); + } - var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.DraftRequired)) - ?? throw new Exception(S["The specified Blog Post doesn't exist anymore. Please create a new Blog Post."]); + return postStruct; + } - await CheckAccessAsync(publish ? CommonPermissions.PublishContent : CommonPermissions.EditContent, user, contentItem); + private async Task MetaWeblogEditPostAsync( + string contentItemId, + string userName, + string password, + XRpcStruct content, + bool publish, + IEnumerable drivers) + { + var user = await ValidateUserAsync(userName, password); - foreach (var driver in _metaWeblogDrivers) - { - driver.EditPost(content, contentItem); - } + var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.DraftRequired)) + ?? throw new Exception(S["The specified Blog Post doesn't exist anymore. Please create a new Blog Post."]); - // Try to get the UTC time zone by default. - var publishedUtc = content.Optional("date_created_gmt"); - if (publishedUtc == null) - { - // take the local one - publishedUtc = content.Optional("dateCreated"); - } - else - { - // Ensure it's read as a UTC time. - publishedUtc = new DateTime(publishedUtc.Value.Ticks, DateTimeKind.Utc); - } + await CheckAccessAsync(publish ? CommonPermissions.PublishContent : CommonPermissions.EditContent, user, contentItem); - if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow)) - { - await _contentManager.PublishAsync(contentItem); - } - else - { - await _contentManager.SaveDraftAsync(contentItem); - } + foreach (var driver in _metaWeblogDrivers) + { + driver.EditPost(content, contentItem); + } - if (publishedUtc != null) - { - contentItem.CreatedUtc = publishedUtc; - } + // Try to get the UTC time zone by default. + var publishedUtc = content.Optional("date_created_gmt"); + if (publishedUtc == null) + { + // take the local one + publishedUtc = content.Optional("dateCreated"); + } + else + { + // Ensure it's read as a UTC time. + publishedUtc = new DateTime(publishedUtc.Value.Ticks, DateTimeKind.Utc); + } - foreach (var driver in drivers) - { - driver.Process(contentItem.Id); - } + if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow)) + { + await _contentManager.PublishAsync(contentItem); + } + else + { + await _contentManager.SaveDraftAsync(contentItem); + } - return true; + if (publishedUtc != null) + { + contentItem.CreatedUtc = publishedUtc; } - private async Task MetaWeblogDeletePostAsync( - string contentItemId, - string userName, - string password, - IEnumerable drivers) + foreach (var driver in drivers) { - var user = await ValidateUserAsync(userName, password); + driver.Process(contentItem.Id); + } - var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.Latest)) - ?? throw new InvalidOperationException("Could not find content item " + contentItemId); + return true; + } - if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.DeleteContent, contentItem)) - { - throw new InvalidOperationException(S["Not authorized to delete this content"].Value); - } + private async Task MetaWeblogDeletePostAsync( + string contentItemId, + string userName, + string password, + IEnumerable drivers) + { + var user = await ValidateUserAsync(userName, password); - foreach (var driver in drivers) - { - driver.Process(contentItem.ContentItemId); - } + var contentItem = (await _contentManager.GetAsync(contentItemId, VersionOptions.Latest)) + ?? throw new InvalidOperationException("Could not find content item " + contentItemId); - await _contentManager.RemoveAsync(contentItem); - return true; + if (!await _authorizationService.AuthorizeAsync(user, CommonPermissions.DeleteContent, contentItem)) + { + throw new InvalidOperationException(S["Not authorized to delete this content"].Value); } - private async Task ValidateUserAsync(string userName, string password) + foreach (var driver in drivers) { - if (!await _membershipService.CheckPasswordAsync(userName, password)) - { - throw new InvalidOperationException(S["The username or e-mail or password provided is incorrect."].Value); - } - - var storeUser = await _membershipService.GetUserAsync(userName); + driver.Process(contentItem.ContentItemId); + } - if (storeUser == null) - { - return null; - } + await _contentManager.RemoveAsync(contentItem); + return true; + } - return await _membershipService.CreateClaimsPrincipal(storeUser); + private async Task ValidateUserAsync(string userName, string password) + { + if (!await _membershipService.CheckPasswordAsync(userName, password)) + { + throw new InvalidOperationException(S["The username or e-mail or password provided is incorrect."].Value); } - private async Task CreateBlogStructAsync(XmlRpcContext context, ContentItem contentItem) + var storeUser = await _membershipService.GetUserAsync(userName); + + if (storeUser == null) { - var metadata = await _contentManager.PopulateAspectAsync(contentItem); + return null; + } - var url = context.Url.Action( - metadata.DisplayRouteValues["action"].ToString(), - metadata.DisplayRouteValues["controller"].ToString(), - metadata.DisplayRouteValues, - context.HttpContext.Request.Scheme); + return await _membershipService.CreateClaimsPrincipal(storeUser); + } - if (contentItem.HasDraft()) - { - url = context.Url.Action("Preview", "Item", new { area = "OrchardCore.Contents", contentItemId = contentItem.ContentItemId }); - } + private async Task CreateBlogStructAsync(XmlRpcContext context, ContentItem contentItem) + { + var metadata = await _contentManager.PopulateAspectAsync(contentItem); - var blogStruct = new XRpcStruct() - .Set("postid", contentItem.ContentItemId) - .Set("link", url) - .Set("permaLink", url); + var url = context.Url.Action( + metadata.DisplayRouteValues["action"].ToString(), + metadata.DisplayRouteValues["controller"].ToString(), + metadata.DisplayRouteValues, + context.HttpContext.Request.Scheme); - if (contentItem.PublishedUtc != null) - { - blogStruct.Set("dateCreated", contentItem.PublishedUtc); - blogStruct.Set("date_created_gmt", contentItem.PublishedUtc); - } + if (contentItem.HasDraft()) + { + url = context.Url.Action("Preview", "Item", new { area = "OrchardCore.Contents", contentItemId = contentItem.ContentItemId }); + } - foreach (var driver in _metaWeblogDrivers) - { - driver.BuildPost(blogStruct, context, contentItem); - } + var blogStruct = new XRpcStruct() + .Set("postid", contentItem.ContentItemId) + .Set("link", url) + .Set("permaLink", url); - return blogStruct; + if (contentItem.PublishedUtc != null) + { + blogStruct.Set("dateCreated", contentItem.PublishedUtc); + blogStruct.Set("date_created_gmt", contentItem.PublishedUtc); } - private async Task CheckAccessAsync(Permission permission, ClaimsPrincipal user, ContentItem contentItem) + foreach (var driver in _metaWeblogDrivers) { - if (!await _authorizationService.AuthorizeAsync(user, permission, contentItem)) - { - throw new InvalidOperationException(S["Not authorized to delete this content"].Value); - } + driver.BuildPost(blogStruct, context, contentItem); } - private async Task> GetContainedContentTypesAsync(ContentItem contentItem) + return blogStruct; + } + + private async Task CheckAccessAsync(Permission permission, ClaimsPrincipal user, ContentItem contentItem) + { + if (!await _authorizationService.AuthorizeAsync(user, permission, contentItem)) { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "ListPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); - var contentTypes = settings.ContainedContentTypes ?? []; + throw new InvalidOperationException(S["Not authorized to delete this content"].Value); + } + } - var definitions = new List(); + private async Task> GetContainedContentTypesAsync(ContentItem contentItem) + { + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "ListPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); + var contentTypes = settings.ContainedContentTypes ?? []; - foreach (var contentType in contentTypes) - { - var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); + var definitions = new List(); - if (definition == null) - { - continue; - } + foreach (var contentType in contentTypes) + { + var definition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentType); - definitions.Add(definition); + if (definition == null) + { + continue; } - return definitions; + definitions.Add(definition); } + + return definitions; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/RemotePublishingStartup.cs b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/RemotePublishingStartup.cs index 943e57d3071..7782a1fb384 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/RemotePublishingStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/RemotePublishing/RemotePublishingStartup.cs @@ -8,26 +8,25 @@ using OrchardCore.Modules; using OrchardCore.XmlRpc; -namespace OrchardCore.Lists.RemotePublishing +namespace OrchardCore.Lists.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class RemotePublishingStartup : StartupBase { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class RemotePublishingStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver(); - } + services.AddScoped(); + services.AddContentPart() + .UseDisplayDriver(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "RSD", - areaName: "OrchardCore.Lists", - pattern: "xmlrpc/metaweblog/{contentItemId}/rsd", - defaults: new { controller = "RemotePublishing", action = "Rsd" } - ); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "RSD", + areaName: "OrchardCore.Lists", + pattern: "xmlrpc/metaweblog/{contentItemId}/rsd", + defaults: new { controller = "RemotePublishing", action = "Rsd" } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Services/ContainerService.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Services/ContainerService.cs index 676ce9d25f3..c2ce3f2e058 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Services/ContainerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Services/ContainerService.cs @@ -13,385 +13,384 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Lists.Services +namespace OrchardCore.Lists.Services; + +public class ContainerService : IContainerService { - public class ContainerService : IContainerService + private readonly YesSql.ISession _session; + private readonly IContentManager _contentManager; + private readonly IHttpContextAccessor _httpContextAccessor; + + public ContainerService( + YesSql.ISession session, + IContentManager contentManager, + IHttpContextAccessor httpContextAccessor) + { + _session = session; + _contentManager = contentManager; + _httpContextAccessor = httpContextAccessor; + } + + public async Task GetNextOrderNumberAsync(string contentItemId) { - private readonly YesSql.ISession _session; - private readonly IContentManager _contentManager; - private readonly IHttpContextAccessor _httpContextAccessor; - - public ContainerService( - YesSql.ISession session, - IContentManager contentManager, - IHttpContextAccessor httpContextAccessor) + var index = await _session.QueryIndex(x => x.ListContentItemId == contentItemId) + .OrderByDescending(x => x.Order) + .FirstOrDefaultAsync(); + + if (index != null) { - _session = session; - _contentManager = contentManager; - _httpContextAccessor = httpContextAccessor; + return index.Order + 1; } - - public async Task GetNextOrderNumberAsync(string contentItemId) + else { - var index = await _session.QueryIndex(x => x.ListContentItemId == contentItemId) - .OrderByDescending(x => x.Order) - .FirstOrDefaultAsync(); - - if (index != null) - { - return index.Order + 1; - } - else - { - return 0; - } + return 0; } + } - public async Task UpdateContentItemOrdersAsync(IEnumerable contentItems, int orderOfFirstItem) + public async Task UpdateContentItemOrdersAsync(IEnumerable contentItems, int orderOfFirstItem) + { + var i = 0; + foreach (var contentItem in contentItems) { - var i = 0; - foreach (var contentItem in contentItems) + var containedPart = contentItem.As(); + if (containedPart != null && containedPart.Order != orderOfFirstItem + i) { - var containedPart = contentItem.As(); - if (containedPart != null && containedPart.Order != orderOfFirstItem + i) - { - containedPart.Order = orderOfFirstItem + i; - containedPart.Apply(); + containedPart.Order = orderOfFirstItem + i; + containedPart.Apply(); - // Keep the published and draft orders the same to avoid confusion in the admin list. - if (!contentItem.IsPublished()) + // Keep the published and draft orders the same to avoid confusion in the admin list. + if (!contentItem.IsPublished()) + { + var publishedItem = await _contentManager.GetAsync(contentItem.ContentItemId, VersionOptions.Published); + if (publishedItem != null) { - var publishedItem = await _contentManager.GetAsync(contentItem.ContentItemId, VersionOptions.Published); - if (publishedItem != null) - { - publishedItem.Alter(x => x.Order = orderOfFirstItem + i); - await _contentManager.UpdateAsync(publishedItem); - } + publishedItem.Alter(x => x.Order = orderOfFirstItem + i); + await _contentManager.UpdateAsync(publishedItem); } - - await _contentManager.UpdateAsync(contentItem); } - i++; + await _contentManager.UpdateAsync(contentItem); } + + i++; } + } - public async Task SetInitialOrder(string contentType) - { - // Set initial order for published and drafts if they have never been published. - var contanerContentItemsQuery = _session.QueryIndex(x => x.ContentType == contentType && (x.Published || x.Latest)); - var containerContentItems = await contanerContentItemsQuery.ListAsync(); + public async Task SetInitialOrder(string contentType) + { + // Set initial order for published and drafts if they have never been published. + var contanerContentItemsQuery = _session.QueryIndex(x => x.ContentType == contentType && (x.Published || x.Latest)); + var containerContentItems = await contanerContentItemsQuery.ListAsync(); - if (!containerContentItems.Any()) - { - return; - } + if (!containerContentItems.Any()) + { + return; + } - // Reduce duplicates to only set order for the published container item and the draft item if it has not been published. - var containerContentItemIds = containerContentItems.Select(x => x.ContentItemId).Distinct(); + // Reduce duplicates to only set order for the published container item and the draft item if it has not been published. + var containerContentItemIds = containerContentItems.Select(x => x.ContentItemId).Distinct(); - var containedItemsQuery = _session.Query() - .With(x => x.ListContentItemId.IsIn(containerContentItemIds)) - .With(ci => ci.Latest || ci.Published) - .OrderByDescending(x => x.CreatedUtc); + var containedItemsQuery = _session.Query() + .With(x => x.ListContentItemId.IsIn(containerContentItemIds)) + .With(ci => ci.Latest || ci.Published) + .OrderByDescending(x => x.CreatedUtc); - // Load items so that loading handlers are invoked. - var contentItemGroups = (await containedItemsQuery.ListAsync(_contentManager)).ToLookup(l => l.As()?.ListContentItemId); + // Load items so that loading handlers are invoked. + var contentItemGroups = (await containedItemsQuery.ListAsync(_contentManager)).ToLookup(l => l.As()?.ListContentItemId); - foreach (var contentItemGroup in contentItemGroups) + foreach (var contentItemGroup in contentItemGroups) + { + var i = 0; + foreach (var contentItem in contentItemGroup) { - var i = 0; - foreach (var contentItem in contentItemGroup) + var containedPart = contentItem.As(); + if (containedPart != null) { - var containedPart = contentItem.As(); - if (containedPart != null) + if (contentItem.Published && contentItem.Latest) + { + containedPart.Order = i; + containedPart.Apply(); + } + else if (contentItem.Latest && !contentItem.Published) { - if (contentItem.Published && contentItem.Latest) + // Update the latest order. + containedPart.Order = i; + containedPart.Apply(); + + // If a published version exists, find it, and set it to the same order as the draft. + var publishedItem = contentItemGroup.FirstOrDefault(p => p.Published == true && p.ContentItemId == contentItem.ContentItemId); + var publishedContainedPart = publishedItem?.As(); + if (publishedContainedPart != null) { - containedPart.Order = i; - containedPart.Apply(); + publishedContainedPart.Order = i; + publishedContainedPart.Apply(); } - else if (contentItem.Latest && !contentItem.Published) + } + else if (contentItem.Published && !contentItem.Latest) + { + // If a latest version exists, it will handle updating the order. + var latestItem = contentItemGroup.FirstOrDefault(l => l.Latest == true && l.ContentItemId == contentItem.ContentItemId); + if (latestItem == null) { - // Update the latest order. + // Apply order to the published item. containedPart.Order = i; containedPart.Apply(); - - // If a published version exists, find it, and set it to the same order as the draft. - var publishedItem = contentItemGroup.FirstOrDefault(p => p.Published == true && p.ContentItemId == contentItem.ContentItemId); - var publishedContainedPart = publishedItem?.As(); - if (publishedContainedPart != null) - { - publishedContainedPart.Order = i; - publishedContainedPart.Apply(); - } } - else if (contentItem.Published && !contentItem.Latest) + else { - // If a latest version exists, it will handle updating the order. - var latestItem = contentItemGroup.FirstOrDefault(l => l.Latest == true && l.ContentItemId == contentItem.ContentItemId); - if (latestItem == null) - { - // Apply order to the published item. - containedPart.Order = i; - containedPart.Apply(); - } - else - { - // Order of this item will be updated when latest is iterated. - continue; - } + // Order of this item will be updated when latest is iterated. + continue; } - - await _session.SaveAsync(contentItem); } - i++; + await _session.SaveAsync(contentItem); } + + i++; } } + } - public async Task> QueryContainedItemsAsync( - string contentItemId, - bool enableOrdering, - PagerSlim pager, - ContainedItemOptions containedItemOptions) - { - ArgumentNullException.ThrowIfNull(containedItemOptions); + public async Task> QueryContainedItemsAsync( + string contentItemId, + bool enableOrdering, + PagerSlim pager, + ContainedItemOptions containedItemOptions) + { + ArgumentNullException.ThrowIfNull(containedItemOptions); - IQuery query = null; - if (pager.Before != null) + IQuery query = null; + if (pager.Before != null) + { + if (enableOrdering) { - if (enableOrdering) - { - var beforeValue = int.Parse(pager.Before); - query = _session.Query() - .With(CreateOrderedContainedPartIndexFilter(beforeValue, null, contentItemId)) - .OrderByDescending(x => x.Order); - } - else - { - var beforeValue = new DateTime(long.Parse(pager.Before)); - query = _session.Query() - .With(x => x.ListContentItemId == contentItemId); + var beforeValue = int.Parse(pager.Before); + query = _session.Query() + .With(CreateOrderedContainedPartIndexFilter(beforeValue, null, contentItemId)) + .OrderByDescending(x => x.Order); + } + else + { + var beforeValue = new DateTime(long.Parse(pager.Before)); + query = _session.Query() + .With(x => x.ListContentItemId == contentItemId); - ApplyPagingContentIndexFilter(beforeValue, null, true, query); - } + ApplyPagingContentIndexFilter(beforeValue, null, true, query); + } - ApplyContainedItemOptionsFilter(containedItemOptions, query); + ApplyContainedItemOptionsFilter(containedItemOptions, query); - // Take() needs to be the last expression in the query otherwise the ORDER BY clause will be - // syntactically incorrect. - query.Take(pager.PageSize + 1); + // Take() needs to be the last expression in the query otherwise the ORDER BY clause will be + // syntactically incorrect. + query.Take(pager.PageSize + 1); - // Load items so that loading handlers are invoked. - var containedItems = await query.ListAsync(_contentManager); + // Load items so that loading handlers are invoked. + var containedItems = await query.ListAsync(_contentManager); - if (!containedItems.Any()) - { - return containedItems; - } + if (!containedItems.Any()) + { + return containedItems; + } - containedItems = containedItems.Reverse(); + containedItems = containedItems.Reverse(); - // There is always an After as we clicked on Before - pager.Before = null; + // There is always an After as we clicked on Before + pager.Before = null; + if (enableOrdering) + { + pager.After = containedItems.Last().As().Order.ToString(); + } + else + { + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); + } + if (containedItems.Count() == pager.PageSize + 1) + { + containedItems = containedItems.Skip(1); if (enableOrdering) { - pager.After = containedItems.Last().As().Order.ToString(); + pager.Before = containedItems.First().As().Order.ToString(); } else { - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - } - if (containedItems.Count() == pager.PageSize + 1) - { - containedItems = containedItems.Skip(1); - if (enableOrdering) - { - pager.Before = containedItems.First().As().Order.ToString(); - } - else - { - pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); - } + pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); } + } - return containedItems; + return containedItems; + } + else if (pager.After != null) + { + if (enableOrdering) + { + var afterValue = int.Parse(pager.After); + query = _session.Query() + .With(CreateOrderedContainedPartIndexFilter(null, afterValue, contentItemId)) + .OrderBy(x => x.Order); } - else if (pager.After != null) + else { - if (enableOrdering) - { - var afterValue = int.Parse(pager.After); - query = _session.Query() - .With(CreateOrderedContainedPartIndexFilter(null, afterValue, contentItemId)) - .OrderBy(x => x.Order); - } - else - { - var afterValue = new DateTime(long.Parse(pager.After)); - query = _session.Query() - .With(CreateOrderedContainedPartIndexFilter(null, null, contentItemId)); + var afterValue = new DateTime(long.Parse(pager.After)); + query = _session.Query() + .With(CreateOrderedContainedPartIndexFilter(null, null, contentItemId)); - ApplyPagingContentIndexFilter(null, afterValue, false, query); - } + ApplyPagingContentIndexFilter(null, afterValue, false, query); + } - ApplyContainedItemOptionsFilter(containedItemOptions, query); + ApplyContainedItemOptionsFilter(containedItemOptions, query); - query.Take(pager.PageSize + 1); + query.Take(pager.PageSize + 1); - // Load items so that loading handlers are invoked. - var containedItems = await query.ListAsync(_contentManager); + // Load items so that loading handlers are invoked. + var containedItems = await query.ListAsync(_contentManager); - if (!containedItems.Any()) - { - return containedItems; - } + if (!containedItems.Any()) + { + return containedItems; + } + + // There is always a Before page as we clicked on After + if (enableOrdering) + { + pager.Before = containedItems.First().As().Order.ToString(); + } + else + { + pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); + } + pager.After = null; - // There is always a Before page as we clicked on After + if (containedItems.Count() == pager.PageSize + 1) + { + containedItems = containedItems.Take(pager.PageSize); if (enableOrdering) { - pager.Before = containedItems.First().As().Order.ToString(); + pager.After = containedItems.Last().As().Order.ToString(); } else { - pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); - } - pager.After = null; - - if (containedItems.Count() == pager.PageSize + 1) - { - containedItems = containedItems.Take(pager.PageSize); - if (enableOrdering) - { - pager.After = containedItems.Last().As().Order.ToString(); - } - else - { - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - } + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); } + } - return containedItems; + return containedItems; + } + else + { + if (enableOrdering) + { + query = _session.Query() + .With(CreateOrderedContainedPartIndexFilter(null, null, contentItemId)) + .OrderBy(x => x.Order); } else { - if (enableOrdering) - { - query = _session.Query() - .With(CreateOrderedContainedPartIndexFilter(null, null, contentItemId)) - .OrderBy(x => x.Order); - } - else - { - query = _session.Query() - .With(x => x.ListContentItemId == contentItemId); + query = _session.Query() + .With(x => x.ListContentItemId == contentItemId); - ApplyPagingContentIndexFilter(null, null, false, query); - } + ApplyPagingContentIndexFilter(null, null, false, query); + } - ApplyContainedItemOptionsFilter(containedItemOptions, query); + ApplyContainedItemOptionsFilter(containedItemOptions, query); - query.Take(pager.PageSize + 1); + query.Take(pager.PageSize + 1); - // Load items so that loading handlers are invoked. - var containedItems = await query.ListAsync(_contentManager); + // Load items so that loading handlers are invoked. + var containedItems = await query.ListAsync(_contentManager); - if (!containedItems.Any()) - { - return containedItems; - } + if (!containedItems.Any()) + { + return containedItems; + } - pager.Before = null; - pager.After = null; + pager.Before = null; + pager.After = null; - if (containedItems.Count() == pager.PageSize + 1) + if (containedItems.Count() == pager.PageSize + 1) + { + containedItems = containedItems.Take(pager.PageSize); + if (enableOrdering) { - containedItems = containedItems.Take(pager.PageSize); - if (enableOrdering) - { - pager.After = containedItems.Last().As().Order.ToString(); - } - else - { - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - } + pager.After = containedItems.Last().As().Order.ToString(); + } + else + { + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); } - - return containedItems; } + + return containedItems; } + } - private static void ApplyPagingContentIndexFilter(DateTime? before, DateTime? after, bool orderByAsc, IQuery query) - { - var indexQuery = query.With(); + private static void ApplyPagingContentIndexFilter(DateTime? before, DateTime? after, bool orderByAsc, IQuery query) + { + var indexQuery = query.With(); - if (before != null) - { - indexQuery.Where(i => i.CreatedUtc > before); - } + if (before != null) + { + indexQuery.Where(i => i.CreatedUtc > before); + } - if (after != null) - { - indexQuery.Where(i => i.CreatedUtc < after); - } + if (after != null) + { + indexQuery.Where(i => i.CreatedUtc < after); + } - if (orderByAsc) - { - indexQuery.OrderBy(i => i.CreatedUtc); - } - else - { - indexQuery.OrderByDescending(i => i.CreatedUtc); - } + if (orderByAsc) + { + indexQuery.OrderBy(i => i.CreatedUtc); } + else + { + indexQuery.OrderByDescending(i => i.CreatedUtc); + } + } - private void ApplyContainedItemOptionsFilter(ContainedItemOptions containedItemOptions, IQuery query) + private void ApplyContainedItemOptionsFilter(ContainedItemOptions containedItemOptions, IQuery query) + { + if (!string.IsNullOrEmpty(containedItemOptions.DisplayText)) { - if (!string.IsNullOrEmpty(containedItemOptions.DisplayText)) - { - query.With(i => i.DisplayText.Contains(containedItemOptions.DisplayText)); - } + query.With(i => i.DisplayText.Contains(containedItemOptions.DisplayText)); + } - switch (containedItemOptions.Status) - { - case ContentsStatus.Published: - query.With(i => i.Published); - break; - case ContentsStatus.Latest: - query.With(i => i.Latest); - break; - case ContentsStatus.Draft: - query.With(i => !i.Published && i.Latest); - break; - case ContentsStatus.Owner: - var currentUserName = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); - - if (currentUserName != null) - { - query.With(i => (i.Published || i.Latest) && i.Owner == currentUserName); - } + switch (containedItemOptions.Status) + { + case ContentsStatus.Published: + query.With(i => i.Published); + break; + case ContentsStatus.Latest: + query.With(i => i.Latest); + break; + case ContentsStatus.Draft: + query.With(i => !i.Published && i.Latest); + break; + case ContentsStatus.Owner: + var currentUserName = _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); + + if (currentUserName != null) + { + query.With(i => (i.Published || i.Latest) && i.Owner == currentUserName); + } - break; - default: - throw new NotSupportedException("Unknown status filter."); - } + break; + default: + throw new NotSupportedException("Unknown status filter."); } + } - private static Expression> CreateOrderedContainedPartIndexFilter(int? before, int? after, string contentItemId) + private static Expression> CreateOrderedContainedPartIndexFilter(int? before, int? after, string contentItemId) + { + if (before != null) { - if (before != null) - { - return x => x.Order < before && x.ListContentItemId == contentItemId; - } - - if (after != null) - { - return x => x.Order > after && x.ListContentItemId == contentItemId; - } + return x => x.Order < before && x.ListContentItemId == contentItemId; + } - return x => x.ListContentItemId == contentItemId; + if (after != null) + { + return x => x.Order > after && x.ListContentItemId == contentItemId; } + + return x => x.ListContentItemId == contentItemId; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Services/IContainerService.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Services/IContainerService.cs index 03f061b8707..1d0b08528a8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Services/IContainerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Services/IContainerService.cs @@ -4,29 +4,28 @@ using OrchardCore.Lists.Models; using OrchardCore.Navigation; -namespace OrchardCore.Lists.Services +namespace OrchardCore.Lists.Services; + +public interface IContainerService { - public interface IContainerService - { - /// - /// Query contained items by page either order by the created utc or order value. - /// - Task> QueryContainedItemsAsync(string contentItemId, bool enableOrdering, PagerSlim pager, ContainedItemOptions containedItemOptions); + /// + /// Query contained items by page either order by the created utc or order value. + /// + Task> QueryContainedItemsAsync(string contentItemId, bool enableOrdering, PagerSlim pager, ContainedItemOptions containedItemOptions); - /// - /// Update the orders of the content items. - /// - Task UpdateContentItemOrdersAsync(IEnumerable contentItems, int orderOfFirstItem); + /// + /// Update the orders of the content items. + /// + Task UpdateContentItemOrdersAsync(IEnumerable contentItems, int orderOfFirstItem); - /// - /// Get the next order number. - /// New or cloned content items are added to the bottom of the list. - /// - Task GetNextOrderNumberAsync(string contentItemId); + /// + /// Get the next order number. + /// New or cloned content items are added to the bottom of the list. + /// + Task GetNextOrderNumberAsync(string contentItemId); - /// - /// Update order of the content items when ordering is enabled. - /// - Task SetInitialOrder(string containerContentType); - } + /// + /// Update order of the content items when ordering is enabled. + /// + Task SetInitialOrder(string containerContentType); } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Services/ListPartContentsAdminListFilter.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Services/ListPartContentsAdminListFilter.cs index 91e4c3f05d8..0ff19a70e05 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Services/ListPartContentsAdminListFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Services/ListPartContentsAdminListFilter.cs @@ -12,39 +12,38 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Lists.Services +namespace OrchardCore.Lists.Services; + +public class ListPartContentsAdminListFilter : IContentsAdminListFilter { - public class ListPartContentsAdminListFilter : IContentsAdminListFilter + private readonly IContentDefinitionManager _contentDefinitionManager; + + public ListPartContentsAdminListFilter(IContentDefinitionManager contentDefinitionManager) { - private readonly IContentDefinitionManager _contentDefinitionManager; + _contentDefinitionManager = contentDefinitionManager; + } - public ListPartContentsAdminListFilter(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) + { + var viewModel = new ListPartContentsAdminFilterViewModel(); + await updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); - public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) + // Show list content items + if (viewModel.ShowListContentTypes) { - var viewModel = new ListPartContentsAdminFilterViewModel(); - await updater.TryUpdateModelAsync(viewModel, nameof(ListPart)); - - // Show list content items - if (viewModel.ShowListContentTypes) - { - var listableTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(x => - x.Parts.Any(p => - p.PartDefinition.Name == nameof(ListPart))) - .Select(x => x.Name); + var listableTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(x => + x.Parts.Any(p => + p.PartDefinition.Name == nameof(ListPart))) + .Select(x => x.Name); - query.With(x => x.ContentType.IsIn(listableTypes)); - } + query.With(x => x.ContentType.IsIn(listableTypes)); + } - // Show contained elements for the specified list - else if (viewModel.ListContentItemId != null) - { - query.With(x => x.ListContentItemId == viewModel.ListContentItemId); - } + // Show contained elements for the specified list + else if (viewModel.ListContentItemId != null) + { + query.With(x => x.ListContentItemId == viewModel.ListContentItemId); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Settings/ListPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Settings/ListPartSettingsDisplayDriver.cs index a70f2862326..3864d482bf0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Settings/ListPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Settings/ListPartSettingsDisplayDriver.cs @@ -9,73 +9,72 @@ using OrchardCore.Lists.Services; using OrchardCore.Lists.ViewModels; -namespace OrchardCore.Lists.Settings +namespace OrchardCore.Lists.Settings; + +public sealed class ListPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class ListPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver - { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IContainerService _containerService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContainerService _containerService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public ListPartSettingsDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - IContainerService containerService, - IStringLocalizer localizer) - { - _contentDefinitionManager = contentDefinitionManager; - _containerService = containerService; - S = localizer; - } + public ListPartSettingsDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + IContainerService containerService, + IStringLocalizer localizer) + { + _contentDefinitionManager = contentDefinitionManager; + _containerService = containerService; + S = localizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("ListPartSettings_Edit", async model => { - return Initialize("ListPartSettings_Edit", async model => - { - model.ListPartSettings = contentTypePartDefinition.GetSettings(); - model.PageSize = model.ListPartSettings.PageSize; - model.EnableOrdering = model.ListPartSettings.EnableOrdering; - model.ContainedContentTypes = model.ListPartSettings.ContainedContentTypes; - model.ShowHeader = model.ListPartSettings.ShowHeader; - model.ContentTypes = []; + model.ListPartSettings = contentTypePartDefinition.GetSettings(); + model.PageSize = model.ListPartSettings.PageSize; + model.EnableOrdering = model.ListPartSettings.EnableOrdering; + model.ContainedContentTypes = model.ListPartSettings.ContainedContentTypes; + model.ShowHeader = model.ListPartSettings.ShowHeader; + model.ContentTypes = []; - foreach (var contentTypeDefinition in await _contentDefinitionManager.ListTypeDefinitionsAsync()) - { - model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); - } - }).Location("Content"); - } + foreach (var contentTypeDefinition in await _contentDefinitionManager.ListTypeDefinitionsAsync()) + { + model.ContentTypes.Add(contentTypeDefinition.Name, contentTypeDefinition.DisplayName); + } + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var settings = contentTypePartDefinition.GetSettings(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var settings = contentTypePartDefinition.GetSettings(); - var model = new ListPartSettingsViewModel(); + var model = new ListPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.ContainedContentTypes, m => m.PageSize, m => m.EnableOrdering, m => m.ShowHeader); + await context.Updater.TryUpdateModelAsync(model, Prefix, m => m.ContainedContentTypes, m => m.PageSize, m => m.EnableOrdering, m => m.ShowHeader); - if (model.ContainedContentTypes == null || model.ContainedContentTypes.Length == 0) - { - context.Updater.ModelState.AddModelError(nameof(model.ContainedContentTypes), S["At least one content type must be selected."]); - } - else + if (model.ContainedContentTypes == null || model.ContainedContentTypes.Length == 0) + { + context.Updater.ModelState.AddModelError(nameof(model.ContainedContentTypes), S["At least one content type must be selected."]); + } + else + { + context.Builder.WithSettings(new ListPartSettings { - context.Builder.WithSettings(new ListPartSettings - { - PageSize = model.PageSize, - EnableOrdering = model.EnableOrdering, - ContainedContentTypes = model.ContainedContentTypes, - ShowHeader = model.ShowHeader, - }); + PageSize = model.PageSize, + EnableOrdering = model.EnableOrdering, + ContainedContentTypes = model.ContainedContentTypes, + ShowHeader = model.ShowHeader, + }); - // Update order of existing content if enable ordering has been turned on. - if (settings.EnableOrdering != model.EnableOrdering && model.EnableOrdering == true) - { - await _containerService.SetInitialOrder(contentTypePartDefinition.ContentTypeDefinition.Name); - } + // Update order of existing content if enable ordering has been turned on. + if (settings.EnableOrdering != model.EnableOrdering && model.EnableOrdering == true) + { + await _containerService.SetInitialOrder(contentTypePartDefinition.ContentTypeDefinition.Name); } - - return Edit(contentTypePartDefinition, context); } + + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Lists/Startup.cs index 5803715c24f..518da3a2259 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/Startup.cs @@ -30,80 +30,79 @@ using OrchardCore.Lists.ViewModels; using OrchardCore.Modules; -namespace OrchardCore.Lists +namespace OrchardCore.Lists; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }) - .AddLiquidFilter("list_count") - .AddLiquidFilter("list_items") - .AddLiquidFilter("container"); + o.MemberAccessStrategy.Register(); + }) + .AddLiquidFilter("list_count") + .AddLiquidFilter("list_items") + .AddLiquidFilter("container"); - services.AddIndexProvider(); - services.AddScoped(); - services.AddScoped(); - services.AddContentPart(); - services.AddScoped(); - services.AddScoped, ListPartContentsAdminListDisplayDriver>(); + services.AddIndexProvider(); + services.AddScoped(); + services.AddScoped(); + services.AddContentPart(); + services.AddScoped(); + services.AddScoped, ListPartContentsAdminListDisplayDriver>(); - // List Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // List Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.AdminMenu")] - public sealed class AdminMenuStartup : StartupBase +[RequireFeatures("OrchardCore.AdminMenu")] +public sealed class AdminMenuStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddAdminNode(); - } + services.AddAdminNode(); } +} - [RequireFeatures("OrchardCore.ContentLocalization")] - public sealed class ContentLocalizationStartup : StartupBase +[RequireFeatures("OrchardCore.ContentLocalization")] +public sealed class ContentLocalizationStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddContentPart() - .AddHandler(); - } + services.AddScoped(); + services.AddScoped(); + services.AddContentPart() + .AddHandler(); } +} - [RequireFeatures("OrchardCore.Feeds")] - public sealed class FeedsStartup : StartupBase +[RequireFeatures("OrchardCore.Feeds")] +public sealed class FeedsStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Feeds - services.AddScoped(); + // Feeds + services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); - } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "ListFeed", - areaName: "OrchardCore.Feeds", - pattern: "Contents/Lists/{contentItemId}/rss", - defaults: new { controller = "Feed", action = "Index", format = "rss" } - ); - } + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); + } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "ListFeed", + areaName: "OrchardCore.Feeds", + pattern: "Contents/Lists/{contentItemId}/rss", + defaults: new { controller = "Feed", action = "Index", format = "rss" } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/EditContainedPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/EditContainedPartViewModel.cs index 2c850fca05b..755fad2db46 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/EditContainedPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/EditContainedPartViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Lists.ViewModels +namespace OrchardCore.Lists.ViewModels; + +public class EditContainedPartViewModel { - public class EditContainedPartViewModel - { - public string ContainerId { get; set; } - public string ContainerContentType { get; set; } - public string ContentType { get; set; } - public bool EnableOrdering { get; set; } - } + public string ContainerId { get; set; } + public string ContainerContentType { get; set; } + public string ContentType { get; set; } + public bool EnableOrdering { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartContentsAdminFilterViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartContentsAdminFilterViewModel.cs index 4b53f9e3898..af4a9e09e5b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartContentsAdminFilterViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartContentsAdminFilterViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Lists.ViewModels +namespace OrchardCore.Lists.ViewModels; + +public class ListPartContentsAdminFilterViewModel { - public class ListPartContentsAdminFilterViewModel - { - public bool ShowListContentTypes { get; set; } - public string ListContentItemId { get; set; } - } + public bool ShowListContentTypes { get; set; } + public string ListContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartFilterViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartFilterViewModel.cs index 364a821394c..122ffec63b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartFilterViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartFilterViewModel.cs @@ -1,10 +1,9 @@ using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.ViewModels +namespace OrchardCore.Lists.ViewModels; + +public class ListPartFilterViewModel { - public class ListPartFilterViewModel - { - public string DisplayText { get; set; } - public ContentsStatus Status { get; set; } = ContentsStatus.Latest; - } + public string DisplayText { get; set; } + public ContentsStatus Status { get; set; } = ContentsStatus.Latest; } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartSettingsViewModel.cs index da07ba8482d..8fb3f38d859 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartSettingsViewModel.cs @@ -1,15 +1,14 @@ using System.Collections.Specialized; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.ViewModels +namespace OrchardCore.Lists.ViewModels; + +public class ListPartSettingsViewModel { - public class ListPartSettingsViewModel - { - public ListPartSettings ListPartSettings { get; set; } - public NameValueCollection ContentTypes { get; set; } - public string[] ContainedContentTypes { get; set; } - public int PageSize { get; set; } - public bool EnableOrdering { get; set; } - public bool ShowHeader { get; set; } - } + public ListPartSettings ListPartSettings { get; set; } + public NameValueCollection ContentTypes { get; set; } + public string[] ContainedContentTypes { get; set; } + public int PageSize { get; set; } + public bool EnableOrdering { get; set; } + public bool ShowHeader { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartViewModel.cs index 02075a30e24..e4a478ce302 100644 --- a/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Lists/ViewModels/ListPartViewModel.cs @@ -4,16 +4,15 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Lists.Models; -namespace OrchardCore.Lists.ViewModels +namespace OrchardCore.Lists.ViewModels; + +public class ListPartViewModel { - public class ListPartViewModel - { - public ListPartFilterViewModel ListPartFilterViewModel { get; set; } - public ListPart ListPart { get; set; } - public IEnumerable ContentItems { get; set; } - public IEnumerable ContainedContentTypeDefinitions { get; set; } - public BuildPartDisplayContext Context { get; set; } - public dynamic Pager { get; set; } - public bool EnableOrdering { get; set; } - } + public ListPartFilterViewModel ListPartFilterViewModel { get; set; } + public ListPart ListPart { get; set; } + public IEnumerable ContentItems { get; set; } + public IEnumerable ContainedContentTypeDefinitions { get; set; } + public BuildPartDisplayContext Context { get; set; } + public dynamic Pager { get; set; } + public bool EnableOrdering { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Localization/AdminMenu.cs index 42b664ac3b0..da14456cb13 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/AdminMenu.cs @@ -4,56 +4,55 @@ using OrchardCore.Localization.Drivers; using OrchardCore.Navigation; -namespace OrchardCore.Localization +namespace OrchardCore.Localization; + +/// +/// Represents a localization menu in the admin site. +/// +public sealed class AdminMenu : INavigationProvider { + private static readonly RouteValueDictionary _routeValues = new() + { + { "area", "OrchardCore.Settings" }, + { "groupId", LocalizationSettingsDisplayDriver.GroupId }, + }; + + internal readonly IStringLocalizer S; + /// - /// Represents a localization menu in the admin site. + /// Creates a new instance of the . /// - public sealed class AdminMenu : INavigationProvider + /// The . + public AdminMenu(IStringLocalizer localizer) { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", LocalizationSettingsDisplayDriver.GroupId }, - }; - - internal readonly IStringLocalizer S; + S = localizer; + } - /// - /// Creates a new instance of the . - /// - /// The . - public AdminMenu(IStringLocalizer localizer) + /// + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - /// - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Localization"], localization => localization - .AddClass("localization") - .Id("localization") - .Add(S["Cultures"], S["Cultures"].PrefixPosition(), cultures => cultures - .AddClass("cultures") - .Id("cultures") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageCultures) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Localization"], localization => localization + .AddClass("localization") + .Id("localization") + .Add(S["Cultures"], S["Cultures"].PrefixPosition(), cultures => cultures + .AddClass("cultures") + .Id("cultures") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageCultures) + .LocalNav() ) ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/Drivers/LocalizationSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Localization/Drivers/LocalizationSettingsDisplayDriver.cs index 4ea3c8ee443..b9feeab7b28 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/Drivers/LocalizationSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/Drivers/LocalizationSettingsDisplayDriver.cs @@ -16,121 +16,120 @@ using OrchardCore.Localization.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Localization.Drivers +namespace OrchardCore.Localization.Drivers; + +/// +/// Represents a for the localization settings section in the admin site. +/// +public sealed class LocalizationSettingsDisplayDriver : SiteDisplayDriver { - /// - /// Represents a for the localization settings section in the admin site. - /// - public sealed class LocalizationSettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "localization"; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly INotifier _notifier; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly CultureOptions _cultureOptions; + + internal readonly IHtmlLocalizer H; + internal readonly IStringLocalizer S; + + protected override string SettingsGroupId + => GroupId; + + public LocalizationSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + INotifier notifier, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IOptions cultureOptions, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer + ) + { + _shellReleaseManager = shellReleaseManager; + _notifier = notifier; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _cultureOptions = cultureOptions.Value; + H = htmlLocalizer; + S = stringLocalizer; + } + + /// + public override async Task EditAsync(ISite site, LocalizationSettings settings, BuildEditorContext context) { - public const string GroupId = "localization"; - - private readonly IShellReleaseManager _shellReleaseManager; - private readonly INotifier _notifier; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly CultureOptions _cultureOptions; - - internal readonly IHtmlLocalizer H; - internal readonly IStringLocalizer S; - - protected override string SettingsGroupId - => GroupId; - - public LocalizationSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - INotifier notifier, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IOptions cultureOptions, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer - ) + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCultures)) { - _shellReleaseManager = shellReleaseManager; - _notifier = notifier; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _cultureOptions = cultureOptions.Value; - H = htmlLocalizer; - S = stringLocalizer; + return null; } - /// - public override async Task EditAsync(ISite site, LocalizationSettings settings, BuildEditorContext context) + context.AddTenantReloadWarningWrapper(); + + return Initialize("LocalizationSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; + model.Cultures = ILocalizationService.GetAllCulturesAndAliases() + .Select(cultureInfo => + { + return new CultureEntry + { + Supported = settings.SupportedCultures.Contains(cultureInfo.Name, StringComparer.OrdinalIgnoreCase), + CultureInfo = cultureInfo, + IsDefault = string.Equals(settings.DefaultCulture, cultureInfo.Name, StringComparison.OrdinalIgnoreCase) + }; + }).ToArray(); - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCultures)) + if (!model.Cultures.Any(x => x.IsDefault)) { - return null; + model.Cultures[0].IsDefault = true; } + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - context.AddTenantReloadWarningWrapper(); + /// + public override async Task UpdateAsync(ISite site, LocalizationSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - return Initialize("LocalizationSettings_Edit", model => - { - model.Cultures = ILocalizationService.GetAllCulturesAndAliases() - .Select(cultureInfo => - { - return new CultureEntry - { - Supported = settings.SupportedCultures.Contains(cultureInfo.Name, StringComparer.OrdinalIgnoreCase), - CultureInfo = cultureInfo, - IsDefault = string.Equals(settings.DefaultCulture, cultureInfo.Name, StringComparison.OrdinalIgnoreCase) - }; - }).ToArray(); - - if (!model.Cultures.Any(x => x.IsDefault)) - { - model.Cultures[0].IsDefault = true; - } - }).Location("Content:2") - .OnGroup(SettingsGroupId); + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCultures)) + { + return null; } - /// - public override async Task UpdateAsync(ISite site, LocalizationSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; + var model = new LocalizationSettingsViewModel(); - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageCultures)) - { - return null; - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - var model = new LocalizationSettingsViewModel(); + var supportedCulture = JConvert.DeserializeObject(model.SupportedCultures); + if (supportedCulture.Length == 0) + { + context.Updater.ModelState.AddModelError("SupportedCultures", S["A culture is required"]); + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + if (context.Updater.ModelState.IsValid) + { + // Invariant culture name is empty so a null value is bound. + settings.DefaultCulture = model.DefaultCulture ?? string.Empty; + settings.SupportedCultures = supportedCulture; - var supportedCulture = JConvert.DeserializeObject(model.SupportedCultures); - if (supportedCulture.Length == 0) + if (!settings.SupportedCultures.Contains(settings.DefaultCulture)) { - context.Updater.ModelState.AddModelError("SupportedCultures", S["A culture is required"]); + settings.DefaultCulture = settings.SupportedCultures[0]; } - if (context.Updater.ModelState.IsValid) - { - // Invariant culture name is empty so a null value is bound. - settings.DefaultCulture = model.DefaultCulture ?? string.Empty; - settings.SupportedCultures = supportedCulture; - - if (!settings.SupportedCultures.Contains(settings.DefaultCulture)) - { - settings.DefaultCulture = settings.SupportedCultures[0]; - } - - // We always release the tenant for the default culture and also supported cultures to take effect. - _shellReleaseManager.RequestRelease(); + // We always release the tenant for the default culture and also supported cultures to take effect. + _shellReleaseManager.RequestRelease(); - // We create a transient scope with the newly selected culture to create a notification that will use it instead of the previous culture. - using (CultureScope.Create(settings.DefaultCulture, ignoreSystemSettings: _cultureOptions.IgnoreSystemSettings)) - { - await _notifier.WarningAsync(H["The site has been restarted for the settings to take effect."]); - } + // We create a transient scope with the newly selected culture to create a notification that will use it instead of the previous culture. + using (CultureScope.Create(settings.DefaultCulture, ignoreSystemSettings: _cultureOptions.IgnoreSystemSettings)) + { + await _notifier.WarningAsync(H["The site has been restarted for the settings to take effect."]); } - - return await EditAsync(site, settings, context); } + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs index 30d9a0d2a83..b436eb8b2b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/CultureQueryObjectType.cs @@ -1,21 +1,20 @@ using GraphQL.Types; -namespace OrchardCore.Localization.GraphQL +namespace OrchardCore.Localization.GraphQL; + +/// +/// Represents a culture object for Graph QL. +/// +public class CultureQueryObjectType : ObjectGraphType { /// - /// Represents a culture object for Graph QL. + /// Creates a new instance of . /// - public class CultureQueryObjectType : ObjectGraphType + public CultureQueryObjectType() { - /// - /// Creates a new instance of . - /// - public CultureQueryObjectType() - { - Name = "SiteCulture"; + Name = "SiteCulture"; - Field("culture").Description("The culture code.").Resolve(context => context.Source.Culture); - Field("default").Description("Whether this is the default culture.").Resolve(context => context.Source.IsDefault); - } + Field("culture").Description("The culture code.").Resolve(context => context.Source.Culture); + Field("default").Description("Whether this is the default culture.").Resolve(context => context.Source.IsDefault); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulture.cs b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulture.cs index 1c776292532..8e0d5e0ee8f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulture.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulture.cs @@ -1,18 +1,17 @@ -namespace OrchardCore.Localization.GraphQL +namespace OrchardCore.Localization.GraphQL; + +/// +/// Represents a culture for the site. +/// +public class SiteCulture { /// - /// Represents a culture for the site. + /// Gets or sets a culture. /// - public class SiteCulture - { - /// - /// Gets or sets a culture. - /// - public string Culture { get; set; } + public string Culture { get; set; } - /// - /// Gets or sets whether the is used as default one. - /// - public bool IsDefault { get; set; } - } + /// + /// Gets or sets whether the is used as default one. + /// + public bool IsDefault { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulturesQuery.cs b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulturesQuery.cs index a6c76859b2c..0af512b0a9e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulturesQuery.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/SiteCulturesQuery.cs @@ -11,69 +11,68 @@ using OrchardCore.Apis.GraphQL.Resolvers; using OrchardCore.ContentManagement.GraphQL.Options; -namespace OrchardCore.Localization.GraphQL +namespace OrchardCore.Localization.GraphQL; + +/// +/// Represents a site cultures for Graph QL. +/// +public class SiteCulturesQuery : ISchemaBuilder { + protected readonly IStringLocalizer S; + private readonly GraphQLContentOptions _graphQLContentOptions; + /// - /// Represents a site cultures for Graph QL. + /// Creates a new instance of the . /// - public class SiteCulturesQuery : ISchemaBuilder + /// The . + /// The . + /// + public SiteCulturesQuery( + IStringLocalizer localizer, + IOptions graphQLContentOptions) { - protected readonly IStringLocalizer S; - private readonly GraphQLContentOptions _graphQLContentOptions; + S = localizer; + _graphQLContentOptions = graphQLContentOptions.Value; + } + + public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - /// - /// Creates a new instance of the . - /// - /// The . - /// The . - /// - public SiteCulturesQuery( - IStringLocalizer localizer, - IOptions graphQLContentOptions) + /// + public Task BuildAsync(ISchema schema) + { + if (_graphQLContentOptions.IsHiddenByDefault("SiteCultures")) { - S = localizer; - _graphQLContentOptions = graphQLContentOptions.Value; + return Task.CompletedTask; } - public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - - /// - public Task BuildAsync(ISchema schema) + var field = new FieldType { - if (_graphQLContentOptions.IsHiddenByDefault("SiteCultures")) - { - return Task.CompletedTask; - } + Name = "SiteCultures", + Description = S["The active cultures configured for the site."], + Type = typeof(ListGraphType), + Resolver = new LockedAsyncFieldResolver>(ResolveAsync) + }; - var field = new FieldType - { - Name = "SiteCultures", - Description = S["The active cultures configured for the site."], - Type = typeof(ListGraphType), - Resolver = new LockedAsyncFieldResolver>(ResolveAsync) - }; + schema.Query.AddField(field); - schema.Query.AddField(field); - - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) - { - var localizationService = resolveContext.RequestServices.GetService(); + private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) + { + var localizationService = resolveContext.RequestServices.GetService(); - var defaultCulture = await localizationService.GetDefaultCultureAsync(); - var supportedCultures = await localizationService.GetSupportedCulturesAsync(); + var defaultCulture = await localizationService.GetDefaultCultureAsync(); + var supportedCultures = await localizationService.GetSupportedCulturesAsync(); - var cultures = supportedCultures.Select(culture => - new SiteCulture - { - Culture = culture, - IsDefault = string.Equals(defaultCulture, culture, StringComparison.OrdinalIgnoreCase), - } - ); + var cultures = supportedCultures.Select(culture => + new SiteCulture + { + Culture = culture, + IsDefault = string.Equals(defaultCulture, culture, StringComparison.OrdinalIgnoreCase), + } + ); - return cultures; - } + return cultures; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/Startup.cs index c1bb967574e..e5fb1e8486a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/GraphQL/Startup.cs @@ -2,19 +2,18 @@ using OrchardCore.Apis.GraphQL; using OrchardCore.Modules; -namespace OrchardCore.Localization.GraphQL +namespace OrchardCore.Localization.GraphQL; + +/// +/// Represents the localization module entry point for Graph QL. +/// +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - /// - /// Represents the localization module entry point for Graph QL. - /// - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + /// + public override void ConfigureServices(IServiceCollection services) { - /// - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddTransient(); - } + services.AddSingleton(); + services.AddTransient(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/Models/CultureEntry.cs b/src/OrchardCore.Modules/OrchardCore.Localization/Models/CultureEntry.cs index c2b28cd7213..5c0226bfaf9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/Models/CultureEntry.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/Models/CultureEntry.cs @@ -1,25 +1,24 @@ using System.Globalization; -namespace OrchardCore.Localization.Models +namespace OrchardCore.Localization.Models; + +/// +/// Represents a culture entry. +/// +public class CultureEntry { /// - /// Represents a culture entry. + /// Gets or sets the instance for the current culture entry. /// - public class CultureEntry - { - /// - /// Gets or sets the instance for the current culture entry. - /// - public CultureInfo CultureInfo { get; set; } + public CultureInfo CultureInfo { get; set; } - /// - /// Gets or sets whether the culture is the default one. - /// - public bool IsDefault { get; set; } + /// + /// Gets or sets whether the culture is the default one. + /// + public bool IsDefault { get; set; } - /// - /// Gets or sets whether the culture is supported. - /// - public bool Supported { get; set; } - } + /// + /// Gets or sets whether the culture is supported. + /// + public bool Supported { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/Models/LocalizationSettings.cs b/src/OrchardCore.Modules/OrchardCore.Localization/Models/LocalizationSettings.cs index 8687da289a8..c3e9526d716 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/Models/LocalizationSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/Models/LocalizationSettings.cs @@ -1,31 +1,30 @@ using System.Globalization; -namespace OrchardCore.Localization.Models +namespace OrchardCore.Localization.Models; + +/// +/// Represents an object to store the localization settings. +/// +public class LocalizationSettings { + private static readonly string[] _defaultSupportedCultures = [CultureInfo.InstalledUICulture.Name]; + /// - /// Represents an object to store the localization settings. + /// Creates a new instance of the . /// - public class LocalizationSettings + public LocalizationSettings() { - private static readonly string[] _defaultSupportedCultures = [CultureInfo.InstalledUICulture.Name]; - - /// - /// Creates a new instance of the . - /// - public LocalizationSettings() - { - DefaultCulture = CultureInfo.InstalledUICulture.Name; - SupportedCultures = _defaultSupportedCultures; - } + DefaultCulture = CultureInfo.InstalledUICulture.Name; + SupportedCultures = _defaultSupportedCultures; + } - /// - /// Gets or sets the default culture of the site. - /// - public string DefaultCulture { get; set; } + /// + /// Gets or sets the default culture of the site. + /// + public string DefaultCulture { get; set; } - /// - /// Gets or sets all the supported cultures of the site. It also contains the default culture. - /// - public string[] SupportedCultures { get; set; } - } + /// + /// Gets or sets all the supported cultures of the site. It also contains the default culture. + /// + public string[] SupportedCultures { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/ModularPoFileLocationProvider.cs b/src/OrchardCore.Modules/OrchardCore.Localization/ModularPoFileLocationProvider.cs index f5b07929a0f..4ba639c56bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/ModularPoFileLocationProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/ModularPoFileLocationProvider.cs @@ -8,80 +8,79 @@ using OrchardCore.Environment.Extensions; using OrchardCore.Environment.Shell; -namespace OrchardCore.Localization +namespace OrchardCore.Localization; + +/// +/// Represents a localization provider to provide the locations of the modules PO files. +/// +public class ModularPoFileLocationProvider : ILocalizationFileLocationProvider { + private const string PoFileExtension = ".po"; + private const string CultureDelimiter = "-"; + + private readonly IExtensionManager _extensionsManager; + private readonly IFileProvider _fileProvider; + private readonly string _resourcesContainer; + private readonly string _applicationDataContainer; + private readonly string _shellDataContainer; + /// - /// Represents a localization provider to provide the locations of the modules PO files. + /// Creates a new intance of the . /// - public class ModularPoFileLocationProvider : ILocalizationFileLocationProvider + /// The . + /// The . + /// The . + /// The . + /// The . + public ModularPoFileLocationProvider( + IExtensionManager extensionsManager, + IHostEnvironment hostingEnvironment, + IOptions shellOptions, + IOptions localizationOptions, + ShellSettings shellSettings) { - private const string PoFileExtension = ".po"; - private const string CultureDelimiter = "-"; - - private readonly IExtensionManager _extensionsManager; - private readonly IFileProvider _fileProvider; - private readonly string _resourcesContainer; - private readonly string _applicationDataContainer; - private readonly string _shellDataContainer; + _extensionsManager = extensionsManager; - /// - /// Creates a new intance of the . - /// - /// The . - /// The . - /// The . - /// The . - /// The . - public ModularPoFileLocationProvider( - IExtensionManager extensionsManager, - IHostEnvironment hostingEnvironment, - IOptions shellOptions, - IOptions localizationOptions, - ShellSettings shellSettings) - { - _extensionsManager = extensionsManager; + _fileProvider = hostingEnvironment.ContentRootFileProvider; + _resourcesContainer = localizationOptions.Value.ResourcesPath; + _applicationDataContainer = shellOptions.Value.ShellsApplicationDataPath; + _shellDataContainer = PathExtensions.Combine(_applicationDataContainer, shellOptions.Value.ShellsContainerName, shellSettings.Name); + } - _fileProvider = hostingEnvironment.ContentRootFileProvider; - _resourcesContainer = localizationOptions.Value.ResourcesPath; - _applicationDataContainer = shellOptions.Value.ShellsApplicationDataPath; - _shellDataContainer = PathExtensions.Combine(_applicationDataContainer, shellOptions.Value.ShellsContainerName, shellSettings.Name); - } + /// + public IEnumerable GetLocations(string cultureName) + { + var poFileName = cultureName + PoFileExtension; + var extensions = _extensionsManager.GetExtensions(); - /// - public IEnumerable GetLocations(string cultureName) + // Load .po files in each extension folder first, based on the extensions order + foreach (var extension in extensions) { - var poFileName = cultureName + PoFileExtension; - var extensions = _extensionsManager.GetExtensions(); - - // Load .po files in each extension folder first, based on the extensions order - foreach (var extension in extensions) - { - // From [Extension]/Localization - yield return _fileProvider.GetFileInfo(PathExtensions.Combine(extension.SubPath, _resourcesContainer, poFileName)); - } + // From [Extension]/Localization + yield return _fileProvider.GetFileInfo(PathExtensions.Combine(extension.SubPath, _resourcesContainer, poFileName)); + } - // Load global .po files from /Localization - yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, poFileName)); + // Load global .po files from /Localization + yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, poFileName)); - // Load tenant-specific .po file from /App_Data/Sites/[Tenant]/Localization - yield return new PhysicalFileInfo(new FileInfo(PathExtensions.Combine(_shellDataContainer, _resourcesContainer, poFileName))); + // Load tenant-specific .po file from /App_Data/Sites/[Tenant]/Localization + yield return new PhysicalFileInfo(new FileInfo(PathExtensions.Combine(_shellDataContainer, _resourcesContainer, poFileName))); - // Load each modules .po file for extending localization when using Orchard Core as a NuGet package - foreach (var extension in extensions) - { - // \src\OrchardCore.Cms.Web\Localization\OrchardCore.Cms.Web\fr-CA.po - yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, extension.Id, poFileName)); + // Load each modules .po file for extending localization when using Orchard Core as a NuGet package + foreach (var extension in extensions) + { + // \src\OrchardCore.Cms.Web\Localization\OrchardCore.Cms.Web\fr-CA.po + yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, extension.Id, poFileName)); - // \src\OrchardCore.Cms.Web\Localization\OrchardCore.Cms.Web-fr-CA.po - yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, extension.Id + CultureDelimiter + poFileName)); - } + // \src\OrchardCore.Cms.Web\Localization\OrchardCore.Cms.Web-fr-CA.po + yield return _fileProvider.GetFileInfo(PathExtensions.Combine(_resourcesContainer, extension.Id + CultureDelimiter + poFileName)); + } - // Load all .po files from a culture specific folder - // e.g, \src\OrchardCore.Cms.Web\Localization\fr-CA\*.po - foreach (var file in _fileProvider.GetDirectoryContents(PathExtensions.Combine(_resourcesContainer, cultureName))) - { - yield return file; - } + // Load all .po files from a culture specific folder + // e.g, \src\OrchardCore.Cms.Web\Localization\fr-CA\*.po + foreach (var file in _fileProvider.GetDirectoryContents(PathExtensions.Combine(_resourcesContainer, cultureName))) + { + yield return file; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/Services/LocalizationService.cs b/src/OrchardCore.Modules/OrchardCore.Localization/Services/LocalizationService.cs index 0de425edef6..234aa5e9f37 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/Services/LocalizationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/Services/LocalizationService.cs @@ -3,51 +3,50 @@ using OrchardCore.Localization.Models; using OrchardCore.Settings; -namespace OrchardCore.Localization.Services +namespace OrchardCore.Localization.Services; + +/// +/// Represents a localization service. +/// +public class LocalizationService : ILocalizationService { + private static readonly string _defaultCulture = CultureInfo.InstalledUICulture.Name; + private static readonly string[] _supportedCultures = [CultureInfo.InstalledUICulture.Name]; + + private readonly ISiteService _siteService; + + private LocalizationSettings _localizationSettings; + /// - /// Represents a localization service. + /// Creates a new instance of . /// - public class LocalizationService : ILocalizationService + /// The . + public LocalizationService(ISiteService siteService) + { + _siteService = siteService; + } + + /// + public async Task GetDefaultCultureAsync() + { + await InitializeLocalizationSettingsAsync(); + + return _localizationSettings.DefaultCulture ?? _defaultCulture; + } + + /// + public async Task GetSupportedCulturesAsync() + { + await InitializeLocalizationSettingsAsync(); + + return _localizationSettings.SupportedCultures == null || _localizationSettings.SupportedCultures.Length == 0 + ? _supportedCultures + : _localizationSettings.SupportedCultures + ; + } + + private async Task InitializeLocalizationSettingsAsync() { - private static readonly string _defaultCulture = CultureInfo.InstalledUICulture.Name; - private static readonly string[] _supportedCultures = [CultureInfo.InstalledUICulture.Name]; - - private readonly ISiteService _siteService; - - private LocalizationSettings _localizationSettings; - - /// - /// Creates a new instance of . - /// - /// The . - public LocalizationService(ISiteService siteService) - { - _siteService = siteService; - } - - /// - public async Task GetDefaultCultureAsync() - { - await InitializeLocalizationSettingsAsync(); - - return _localizationSettings.DefaultCulture ?? _defaultCulture; - } - - /// - public async Task GetSupportedCulturesAsync() - { - await InitializeLocalizationSettingsAsync(); - - return _localizationSettings.SupportedCultures == null || _localizationSettings.SupportedCultures.Length == 0 - ? _supportedCultures - : _localizationSettings.SupportedCultures - ; - } - - private async Task InitializeLocalizationSettingsAsync() - { - _localizationSettings ??= await _siteService.GetSettingsAsync(); - } + _localizationSettings ??= await _siteService.GetSettingsAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Localization/Startup.cs index 22c16472e0e..d8be05ecb8a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/Startup.cs @@ -18,86 +18,85 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.Localization +namespace OrchardCore.Localization; + +/// +/// Represents a localization module entry point. +/// +public sealed class Startup : StartupBase { - /// - /// Represents a localization module entry point. - /// - public sealed class Startup : StartupBase + public override int ConfigureOrder => -100; + + /// + public override void ConfigureServices(IServiceCollection services) { - public override int ConfigureOrder => -100; + services.AddScoped, LocalizationSettingsDisplayDriver>(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - /// - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped, LocalizationSettingsDisplayDriver>(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddPortableObjectLocalization(options => options.ResourcesPath = "Localization"). + AddDataAnnotationsPortableObjectLocalization(); - services.AddPortableObjectLocalization(options => options.ResourcesPath = "Localization"). - AddDataAnnotationsPortableObjectLocalization(); + services.Replace(ServiceDescriptor.Singleton()); + } - services.Replace(ServiceDescriptor.Singleton()); - } + /// + public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var localizationService = serviceProvider.GetService(); - /// - public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - var localizationService = serviceProvider.GetService(); + var defaultCulture = await localizationService.GetDefaultCultureAsync(); + var supportedCultures = await localizationService.GetSupportedCulturesAsync(); - var defaultCulture = await localizationService.GetDefaultCultureAsync(); - var supportedCultures = await localizationService.GetSupportedCulturesAsync(); + var cultureOptions = serviceProvider.GetService>().Value; + var localizationOptions = serviceProvider.GetService>().Value; - var cultureOptions = serviceProvider.GetService>().Value; - var localizationOptions = serviceProvider.GetService>().Value; + localizationOptions.CultureInfoUseUserOverride = !cultureOptions.IgnoreSystemSettings; + localizationOptions + .SetDefaultCulture(defaultCulture) + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures); - localizationOptions.CultureInfoUseUserOverride = !cultureOptions.IgnoreSystemSettings; - localizationOptions - .SetDefaultCulture(defaultCulture) - .AddSupportedCultures(supportedCultures) - .AddSupportedUICultures(supportedCultures); + app.UseRequestLocalization(localizationOptions); + } +} - app.UseRequestLocalization(localizationOptions); - } +[RequireFeatures("OrchardCore.Deployment")] +public sealed class LocalizationDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddSiteSettingsPropertyDeploymentStep(S => S["Culture settings"], S => S["Exports the culture settings."]); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class LocalizationDeploymentStartup : StartupBase +[Feature("OrchardCore.Localization.ContentLanguageHeader")] +public sealed class ContentLanguageHeaderStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Culture settings"], S => S["Exports the culture settings."]); - } + services.Configure(options => options.ApplyCurrentCultureToResponseHeaders = true); } +} - [Feature("OrchardCore.Localization.ContentLanguageHeader")] - public sealed class ContentLanguageHeaderStartup : StartupBase +[Feature("OrchardCore.Localization.AdminCulturePicker")] +public sealed class CulturePickerStartup : StartupBase +{ + private readonly ShellSettings _shellSettings; + private readonly AdminOptions _adminOptions; + + public CulturePickerStartup(IOptions adminOptions, ShellSettings shellSettings) { - public override void ConfigureServices(IServiceCollection services) - { - services.Configure(options => options.ApplyCurrentCultureToResponseHeaders = true); - } + _shellSettings = shellSettings; + _adminOptions = adminOptions.Value; } - [Feature("OrchardCore.Localization.AdminCulturePicker")] - public sealed class CulturePickerStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - private readonly ShellSettings _shellSettings; - private readonly AdminOptions _adminOptions; - - public CulturePickerStartup(IOptions adminOptions, ShellSettings shellSettings) - { - _shellSettings = shellSettings; - _adminOptions = adminOptions.Value; - } - - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped, AdminCulturePickerNavbarDisplayDriver>(); - - services.Configure(options => - options.AddInitialRequestCultureProvider(new AdminCookieCultureProvider(_shellSettings, _adminOptions))); - } + services.AddScoped, AdminCulturePickerNavbarDisplayDriver>(); + + services.Configure(options => + options.AddInitialRequestCultureProvider(new AdminCookieCultureProvider(_shellSettings, _adminOptions))); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Localization/ViewModels/LocalizationSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Localization/ViewModels/LocalizationSettingsViewModel.cs index a746eb05b54..28acc99fed2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Localization/ViewModels/LocalizationSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Localization/ViewModels/LocalizationSettingsViewModel.cs @@ -1,28 +1,27 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Localization.Models; -namespace OrchardCore.Localization.ViewModels +namespace OrchardCore.Localization.ViewModels; + +/// +/// Represents a view model for the localization settings. +/// +public class LocalizationSettingsViewModel { /// - /// Represents a view model for the localization settings. + /// /// - public class LocalizationSettingsViewModel - { - /// - /// - /// - [BindNever] - public CultureEntry[] Cultures { get; set; } = []; + [BindNever] + public CultureEntry[] Cultures { get; set; } = []; - /// - /// Gets or sets all the supported cultures of the site. It also contains the default culture. - /// - /// This property is a json array that is set in the editor. - public string SupportedCultures { get; set; } + /// + /// Gets or sets all the supported cultures of the site. It also contains the default culture. + /// + /// This property is a json array that is set in the editor. + public string SupportedCultures { get; set; } - /// - /// Gets or sets the default culture of the site. - /// - public string DefaultCulture { get; set; } - } + /// + /// Gets or sets the default culture of the site. + /// + public string DefaultCulture { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownBodyPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownBodyPartDisplayDriver.cs index 2f95b24d24d..457e651edef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownBodyPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownBodyPartDisplayDriver.cs @@ -18,100 +18,99 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Markdown.Drivers +namespace OrchardCore.Markdown.Drivers; + +public sealed class MarkdownBodyPartDisplayDriver : ContentPartDisplayDriver { - public sealed class MarkdownBodyPartDisplayDriver : ContentPartDisplayDriver + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly IShortcodeService _shortcodeService; + private readonly IMarkdownService _markdownService; + + internal readonly IStringLocalizer S; + + public MarkdownBodyPartDisplayDriver(ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder, + IHtmlSanitizerService htmlSanitizerService, + IShortcodeService shortcodeService, + IMarkdownService markdownService, + IStringLocalizer localizer) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + _htmlSanitizerService = htmlSanitizerService; + _shortcodeService = shortcodeService; + _markdownService = markdownService; + S = localizer; + } + + public override IDisplayResult Display(MarkdownBodyPart markdownBodyPart, BuildPartDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), m => BuildViewModel(m, markdownBodyPart, context)) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } + + public override IDisplayResult Edit(MarkdownBodyPart markdownBodyPart, BuildPartEditorContext context) { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly IShortcodeService _shortcodeService; - private readonly IMarkdownService _markdownService; - - internal readonly IStringLocalizer S; - - public MarkdownBodyPartDisplayDriver(ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder, - IHtmlSanitizerService htmlSanitizerService, - IShortcodeService shortcodeService, - IMarkdownService markdownService, - IStringLocalizer localizer) + return Initialize(GetEditorShapeType(context), model => { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - _htmlSanitizerService = htmlSanitizerService; - _shortcodeService = shortcodeService; - _markdownService = markdownService; - S = localizer; - } + model.Markdown = markdownBodyPart.Markdown; + model.ContentItem = markdownBodyPart.ContentItem; + model.MarkdownBodyPart = markdownBodyPart; + model.TypePartDefinition = context.TypePartDefinition; + }); + } - public override IDisplayResult Display(MarkdownBodyPart markdownBodyPart, BuildPartDisplayContext context) + public override async Task UpdateAsync(MarkdownBodyPart model, UpdatePartEditorContext context) + { + var viewModel = new MarkdownBodyPartViewModel(); + + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, vm => vm.Markdown); + + if (!string.IsNullOrEmpty(viewModel.Markdown) && !_liquidTemplateManager.Validate(viewModel.Markdown, out var errors)) { - return Initialize(GetDisplayShapeType(context), m => BuildViewModel(m, markdownBodyPart, context)) - .Location("Detail", "Content") - .Location("Summary", "Content"); + var partName = context.TypePartDefinition.DisplayName(); + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Markdown), S["{0} doesn't contain a valid Liquid expression. Details: {1}", partName, string.Join(" ", errors)]); } - - public override IDisplayResult Edit(MarkdownBodyPart markdownBodyPart, BuildPartEditorContext context) + else { - return Initialize(GetEditorShapeType(context), model => - { - model.Markdown = markdownBodyPart.Markdown; - model.ContentItem = markdownBodyPart.ContentItem; - model.MarkdownBodyPart = markdownBodyPart; - model.TypePartDefinition = context.TypePartDefinition; - }); + model.Markdown = viewModel.Markdown; } - public override async Task UpdateAsync(MarkdownBodyPart model, UpdatePartEditorContext context) - { - var viewModel = new MarkdownBodyPartViewModel(); + return Edit(model, context); + } - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, vm => vm.Markdown); + private async ValueTask BuildViewModel(MarkdownBodyPartViewModel model, MarkdownBodyPart markdownBodyPart, BuildPartDisplayContext context) + { + model.Markdown = markdownBodyPart.Markdown; + model.MarkdownBodyPart = markdownBodyPart; + model.ContentItem = markdownBodyPart.ContentItem; - if (!string.IsNullOrEmpty(viewModel.Markdown) && !_liquidTemplateManager.Validate(viewModel.Markdown, out var errors)) - { - var partName = context.TypePartDefinition.DisplayName(); - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Markdown), S["{0} doesn't contain a valid Liquid expression. Details: {1}", partName, string.Join(" ", errors)]); - } - else - { - model.Markdown = viewModel.Markdown; - } + // The default Markdown option is to entity escape html + // so filters must be run after the markdown has been processed. + model.Html = _markdownService.ToHtml(model.Markdown ?? ""); - return Edit(model, context); - } + var settings = context.TypePartDefinition.GetSettings(); - private async ValueTask BuildViewModel(MarkdownBodyPartViewModel model, MarkdownBodyPart markdownBodyPart, BuildPartDisplayContext context) + // The liquid rendering is for backwards compatibility and can be removed in a future version. + if (!settings.SanitizeHtml) { - model.Markdown = markdownBodyPart.Markdown; - model.MarkdownBodyPart = markdownBodyPart; - model.ContentItem = markdownBodyPart.ContentItem; - - // The default Markdown option is to entity escape html - // so filters must be run after the markdown has been processed. - model.Html = _markdownService.ToHtml(model.Markdown ?? ""); - - var settings = context.TypePartDefinition.GetSettings(); + model.Html = await _liquidTemplateManager.RenderStringAsync(model.Html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + } - // The liquid rendering is for backwards compatibility and can be removed in a future version. - if (!settings.SanitizeHtml) + model.Html = await _shortcodeService.ProcessAsync(model.Html, + new Context { - model.Html = await _liquidTemplateManager.RenderStringAsync(model.Html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } - - model.Html = await _shortcodeService.ProcessAsync(model.Html, - new Context - { - ["ContentItem"] = markdownBodyPart.ContentItem, - ["TypePartDefinition"] = context.TypePartDefinition - }); - - if (settings.SanitizeHtml) - { - model.Html = _htmlSanitizerService.Sanitize(model.Html ?? ""); - } + ["ContentItem"] = markdownBodyPart.ContentItem, + ["TypePartDefinition"] = context.TypePartDefinition + }); + + if (settings.SanitizeHtml) + { + model.Html = _htmlSanitizerService.Sanitize(model.Html ?? ""); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownFieldDisplayDriver.cs index 991c587e73f..8c429666560 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Drivers/MarkdownFieldDisplayDriver.cs @@ -18,99 +18,98 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Markdown.Drivers +namespace OrchardCore.Markdown.Drivers; + +public sealed class MarkdownFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class MarkdownFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly IShortcodeService _shortcodeService; - private readonly IMarkdownService _markdownService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly IShortcodeService _shortcodeService; + private readonly IMarkdownService _markdownService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public MarkdownFieldDisplayDriver(ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder, - IHtmlSanitizerService htmlSanitizerService, - IShortcodeService shortcodeService, - IMarkdownService markdownService, - IStringLocalizer localizer) - { - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - _htmlSanitizerService = htmlSanitizerService; - _shortcodeService = shortcodeService; - _markdownService = markdownService; - S = localizer; - } + public MarkdownFieldDisplayDriver(ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder, + IHtmlSanitizerService htmlSanitizerService, + IShortcodeService shortcodeService, + IMarkdownService markdownService, + IStringLocalizer localizer) + { + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + _htmlSanitizerService = htmlSanitizerService; + _shortcodeService = shortcodeService; + _markdownService = markdownService; + S = localizer; + } - public override IDisplayResult Display(MarkdownField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(MarkdownField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), async model => { - return Initialize(GetDisplayShapeType(context), async model => - { - - var settings = context.PartFieldDefinition.GetSettings(); - model.Markdown = field.Markdown; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - // The default Markdown option is to entity escape html - // so filters must be run after the markdown has been processed. - model.Html = _markdownService.ToHtml(model.Markdown ?? string.Empty); + var settings = context.PartFieldDefinition.GetSettings(); + model.Markdown = field.Markdown; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; - // The liquid rendering is for backwards compatibility and can be removed in a future version. - if (!settings.SanitizeHtml) - { - model.Markdown = await _liquidTemplateManager.RenderStringAsync(model.Html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(field.ContentItem) }); - } + // The default Markdown option is to entity escape html + // so filters must be run after the markdown has been processed. + model.Html = _markdownService.ToHtml(model.Markdown ?? string.Empty); - model.Html = await _shortcodeService.ProcessAsync(model.Html, - new Context - { - ["ContentItem"] = field.ContentItem, - ["PartFieldDefinition"] = context.PartFieldDefinition - }); + // The liquid rendering is for backwards compatibility and can be removed in a future version. + if (!settings.SanitizeHtml) + { + model.Markdown = await _liquidTemplateManager.RenderStringAsync(model.Html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(field.ContentItem) }); + } - if (settings.SanitizeHtml) + model.Html = await _shortcodeService.ProcessAsync(model.Html, + new Context { - model.Html = _htmlSanitizerService.Sanitize(model.Html ?? string.Empty); - } - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + ["ContentItem"] = field.ContentItem, + ["PartFieldDefinition"] = context.PartFieldDefinition + }); - public override IDisplayResult Edit(MarkdownField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => + if (settings.SanitizeHtml) { - model.Markdown = field.Markdown; - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + model.Html = _htmlSanitizerService.Sanitize(model.Html ?? string.Empty); + } + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override async Task UpdateAsync(MarkdownField field, UpdateFieldEditorContext context) + public override IDisplayResult Edit(MarkdownField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - var viewModel = new EditMarkdownFieldViewModel(); + model.Markdown = field.Markdown; + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, vm => vm.Markdown); + public override async Task UpdateAsync(MarkdownField field, UpdateFieldEditorContext context) + { + var viewModel = new EditMarkdownFieldViewModel(); - if (!string.IsNullOrEmpty(viewModel.Markdown) && !_liquidTemplateManager.Validate(viewModel.Markdown, out var errors)) - { - var fieldName = context.PartFieldDefinition.DisplayName(); - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Markdown), S["{0} field doesn't contain a valid Liquid expression. Details: {1}", fieldName, string.Join(" ", errors)]); - } - else - { - field.Markdown = viewModel.Markdown; - } + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, vm => vm.Markdown); - return Edit(field, context); + if (!string.IsNullOrEmpty(viewModel.Markdown) && !_liquidTemplateManager.Validate(viewModel.Markdown, out var errors)) + { + var fieldName = context.PartFieldDefinition.DisplayName(); + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Markdown), S["{0} field doesn't contain a valid Liquid expression. Details: {1}", fieldName, string.Join(" ", errors)]); } + else + { + field.Markdown = viewModel.Markdown; + } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Fields/MarkdownField.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Fields/MarkdownField.cs index 84b4e8be93d..92813693ccc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Fields/MarkdownField.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Fields/MarkdownField.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Markdown.Fields +namespace OrchardCore.Markdown.Fields; + +public class MarkdownField : ContentField { - public class MarkdownField : ContentField - { - public string Markdown { get; set; } - } + public string Markdown { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Filters/Markdownify.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Filters/Markdownify.cs index 65b1cca1fe7..2b097ef430e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Filters/Markdownify.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Filters/Markdownify.cs @@ -4,20 +4,19 @@ using OrchardCore.Liquid; using OrchardCore.Markdown.Services; -namespace OrchardCore.Markdown.Filters +namespace OrchardCore.Markdown.Filters; + +public class Markdownify : ILiquidFilter { - public class Markdownify : ILiquidFilter - { - private readonly IMarkdownService _markdownService; + private readonly IMarkdownService _markdownService; - public Markdownify(IMarkdownService markdownService) - { - _markdownService = markdownService; - } + public Markdownify(IMarkdownService markdownService) + { + _markdownService = markdownService; + } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) - { - return new ValueTask(new StringValue(_markdownService.ToHtml(input.ToStringValue()))); - } + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + return new ValueTask(new StringValue(_markdownService.ToHtml(input.ToStringValue()))); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownBodyQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownBodyQueryObjectType.cs index 7d1c0547ac7..b8ddaea0b81 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownBodyQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownBodyQueryObjectType.cs @@ -19,73 +19,72 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Markdown.GraphQL +namespace OrchardCore.Markdown.GraphQL; + +public class MarkdownBodyQueryObjectType : ObjectGraphType { - public class MarkdownBodyQueryObjectType : ObjectGraphType + public MarkdownBodyQueryObjectType(IStringLocalizer S) { - public MarkdownBodyQueryObjectType(IStringLocalizer S) - { - Name = nameof(MarkdownBodyPart); - Description = S["Content stored as Markdown. You can also query the HTML interpreted version of Markdown."]; + Name = nameof(MarkdownBodyPart); + Description = S["Content stored as Markdown. You can also query the HTML interpreted version of Markdown."]; - Field("markdown", x => x.Markdown, nullable: true) - .Description(S["the markdown value"]); - Field("html") - .Description(S["the HTML representation of the markdown content"]) - .ResolveLockedAsync(ToHtml); - } + Field("markdown", x => x.Markdown, nullable: true) + .Description(S["the markdown value"]); + Field("html") + .Description(S["the HTML representation of the markdown content"]) + .ResolveLockedAsync(ToHtml); + } - private static async ValueTask ToHtml(IResolveFieldContext ctx) + private static async ValueTask ToHtml(IResolveFieldContext ctx) + { + if (string.IsNullOrEmpty(ctx.Source.Markdown)) { - if (string.IsNullOrEmpty(ctx.Source.Markdown)) - { - return ctx.Source.Markdown; - } + return ctx.Source.Markdown; + } - var serviceProvider = ctx.RequestServices; - var markdownService = serviceProvider.GetRequiredService(); - var shortcodeService = serviceProvider.GetRequiredService(); - var contentDefinitionManager = serviceProvider.GetRequiredService(); + var serviceProvider = ctx.RequestServices; + var markdownService = serviceProvider.GetRequiredService(); + var shortcodeService = serviceProvider.GetRequiredService(); + var contentDefinitionManager = serviceProvider.GetRequiredService(); - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "MarkdownBodyPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "MarkdownBodyPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); - // The default Markdown option is to entity escape html - // so filters must be run after the markdown has been processed. - var html = markdownService.ToHtml(ctx.Source.Markdown); + // The default Markdown option is to entity escape html + // so filters must be run after the markdown has been processed. + var html = markdownService.ToHtml(ctx.Source.Markdown); - // The liquid rendering is for backwards compatibility and can be removed in a future version. - if (!settings.SanitizeHtml) + // The liquid rendering is for backwards compatibility and can be removed in a future version. + if (!settings.SanitizeHtml) + { + var liquidTemplateManager = serviceProvider.GetService(); + var htmlEncoder = serviceProvider.GetService(); + var model = new MarkdownBodyPartViewModel() { - var liquidTemplateManager = serviceProvider.GetService(); - var htmlEncoder = serviceProvider.GetService(); - var model = new MarkdownBodyPartViewModel() - { - Markdown = ctx.Source.Markdown, - Html = html, - MarkdownBodyPart = ctx.Source, - ContentItem = ctx.Source.ContentItem - }; - - html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } + Markdown = ctx.Source.Markdown, + Html = html, + MarkdownBodyPart = ctx.Source, + ContentItem = ctx.Source.ContentItem + }; - html = await shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = ctx.Source.ContentItem, - ["PartFieldDefinition"] = contentTypePartDefinition - }); + html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + } - if (settings.SanitizeHtml) + html = await shortcodeService.ProcessAsync(html, + new Context { - var htmlSanitizerService = serviceProvider.GetRequiredService(); - html = htmlSanitizerService.Sanitize(html); - } + ["ContentItem"] = ctx.Source.ContentItem, + ["PartFieldDefinition"] = contentTypePartDefinition + }); - return html; + if (settings.SanitizeHtml) + { + var htmlSanitizerService = serviceProvider.GetRequiredService(); + html = htmlSanitizerService.Sanitize(html); } + + return html; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownFieldQueryObjectType.cs index 3248878322d..bd971b2afdd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/MarkdownFieldQueryObjectType.cs @@ -21,84 +21,83 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Markdown.GraphQL +namespace OrchardCore.Markdown.GraphQL; + +public class MarkdownFieldQueryObjectType : ObjectGraphType { - public class MarkdownFieldQueryObjectType : ObjectGraphType + public MarkdownFieldQueryObjectType(IStringLocalizer S) { - public MarkdownFieldQueryObjectType(IStringLocalizer S) - { - Name = nameof(MarkdownField); - Description = S["Content stored as Markdown. You can also query the HTML interpreted version of Markdown."]; + Name = nameof(MarkdownField); + Description = S["Content stored as Markdown. You can also query the HTML interpreted version of Markdown."]; - Field("markdown", x => x.Markdown, nullable: true) - .Description(S["the markdown value"]); - Field("html") - .Description(S["the HTML representation of the markdown content"]) - .ResolveLockedAsync(ToHtml); - } + Field("markdown", x => x.Markdown, nullable: true) + .Description(S["the markdown value"]); + Field("html") + .Description(S["the HTML representation of the markdown content"]) + .ResolveLockedAsync(ToHtml); + } - private static async ValueTask ToHtml(IResolveFieldContext ctx) + private static async ValueTask ToHtml(IResolveFieldContext ctx) + { + if (string.IsNullOrEmpty(ctx.Source.Markdown)) { - if (string.IsNullOrEmpty(ctx.Source.Markdown)) - { - return ctx.Source.Markdown; - } - - var serviceProvider = ctx.RequestServices; - var markdownService = serviceProvider.GetRequiredService(); - var shortcodeService = serviceProvider.GetRequiredService(); + return ctx.Source.Markdown; + } - var contentDefinitionManager = serviceProvider.GetRequiredService(); + var serviceProvider = ctx.RequestServices; + var markdownService = serviceProvider.GetRequiredService(); + var shortcodeService = serviceProvider.GetRequiredService(); - var jObject = (JsonObject)ctx.Source.Content; - // The JObject.Path is consistent here even when contained in a bag part. - var jsonPath = jObject.GetNormalizedPath(); - var paths = jsonPath.Split('.'); - var partName = paths[0]; - var fieldName = paths[1]; - var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); - var contentPartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, partName, StringComparison.Ordinal)); - var contentPartFieldDefinition = contentPartDefinition.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, fieldName, StringComparison.Ordinal)); + var contentDefinitionManager = serviceProvider.GetRequiredService(); - var settings = contentPartFieldDefinition.GetSettings(); + var jObject = (JsonObject)ctx.Source.Content; + // The JObject.Path is consistent here even when contained in a bag part. + var jsonPath = jObject.GetNormalizedPath(); + var paths = jsonPath.Split('.'); + var partName = paths[0]; + var fieldName = paths[1]; + var contentTypeDefinition = await contentDefinitionManager.GetTypeDefinitionAsync(ctx.Source.ContentItem.ContentType); + var contentPartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.Name, partName, StringComparison.Ordinal)); + var contentPartFieldDefinition = contentPartDefinition.PartDefinition.Fields.FirstOrDefault(x => string.Equals(x.Name, fieldName, StringComparison.Ordinal)); - // The default Markdown option is to entity escape html - // so filters must be run after the markdown has been processed. - var html = markdownService.ToHtml(ctx.Source.Markdown); + var settings = contentPartFieldDefinition.GetSettings(); - // The liquid rendering is for backwards compatibility and can be removed in a future version. - if (!settings.SanitizeHtml) - { - var liquidTemplateManager = serviceProvider.GetService(); - var htmlEncoder = serviceProvider.GetService(); + // The default Markdown option is to entity escape html + // so filters must be run after the markdown has been processed. + var html = markdownService.ToHtml(ctx.Source.Markdown); - var model = new MarkdownFieldViewModel() - { - Markdown = ctx.Source.Markdown, - Html = html, - Field = ctx.Source, - Part = ctx.Source.ContentItem.Get(partName), - PartFieldDefinition = contentPartFieldDefinition - }; + // The liquid rendering is for backwards compatibility and can be removed in a future version. + if (!settings.SanitizeHtml) + { + var liquidTemplateManager = serviceProvider.GetService(); + var htmlEncoder = serviceProvider.GetService(); - html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(ctx.Source.ContentItem) }); - } + var model = new MarkdownFieldViewModel() + { + Markdown = ctx.Source.Markdown, + Html = html, + Field = ctx.Source, + Part = ctx.Source.ContentItem.Get(partName), + PartFieldDefinition = contentPartFieldDefinition + }; - html = await shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = ctx.Source.ContentItem, - ["PartFieldDefinition"] = contentPartFieldDefinition - }); + html = await liquidTemplateManager.RenderStringAsync(html, htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(ctx.Source.ContentItem) }); + } - if (settings.SanitizeHtml) + html = await shortcodeService.ProcessAsync(html, + new Context { - var htmlSanitizerService = serviceProvider.GetRequiredService(); - html = htmlSanitizerService.Sanitize(html); - } + ["ContentItem"] = ctx.Source.ContentItem, + ["PartFieldDefinition"] = contentPartFieldDefinition + }); - return html; + if (settings.SanitizeHtml) + { + var htmlSanitizerService = serviceProvider.GetRequiredService(); + html = htmlSanitizerService.Sanitize(html); } + + return html; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/Startup.cs index 531aaed4cb9..aaba84546b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/GraphQL/Startup.cs @@ -4,15 +4,14 @@ using OrchardCore.Markdown.Models; using OrchardCore.Modules; -namespace OrchardCore.Markdown.GraphQL +namespace OrchardCore.Markdown.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddObjectGraphType(); - } + services.AddObjectGraphType(); + services.AddObjectGraphType(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Handlers/MarkdownBodyPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Handlers/MarkdownBodyPartHandler.cs index fb896d144bc..9c40ba776cd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Handlers/MarkdownBodyPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Handlers/MarkdownBodyPartHandler.cs @@ -17,80 +17,79 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Markdown.Handlers +namespace OrchardCore.Markdown.Handlers; + +public class MarkdownBodyPartHandler : ContentPartHandler { - public class MarkdownBodyPartHandler : ContentPartHandler - { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IShortcodeService _shortcodeService; - private readonly IMarkdownService _markdownService; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IShortcodeService _shortcodeService; + private readonly IMarkdownService _markdownService; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - public MarkdownBodyPartHandler(IContentDefinitionManager contentDefinitionManager, - IShortcodeService shortcodeService, - IMarkdownService markdownService, - IHtmlSanitizerService htmlSanitizerService, - ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder) - { - _contentDefinitionManager = contentDefinitionManager; - _shortcodeService = shortcodeService; - _markdownService = markdownService; - _htmlSanitizerService = htmlSanitizerService; - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; - } + public MarkdownBodyPartHandler(IContentDefinitionManager contentDefinitionManager, + IShortcodeService shortcodeService, + IMarkdownService markdownService, + IHtmlSanitizerService htmlSanitizerService, + ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder) + { + _contentDefinitionManager = contentDefinitionManager; + _shortcodeService = shortcodeService; + _markdownService = markdownService; + _htmlSanitizerService = htmlSanitizerService; + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, MarkdownBodyPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, MarkdownBodyPart part) + { + return context.ForAsync(async bodyAspect => { - return context.ForAsync(async bodyAspect => + try { - try - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); - var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "MarkdownBodyPart", StringComparison.Ordinal)); - var settings = contentTypePartDefinition.GetSettings(); + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(part.ContentItem.ContentType); + var contentTypePartDefinition = contentTypeDefinition.Parts.FirstOrDefault(x => string.Equals(x.PartDefinition.Name, "MarkdownBodyPart", StringComparison.Ordinal)); + var settings = contentTypePartDefinition.GetSettings(); - // The default Markdown option is to entity escape html - // so filters must be run after the markdown has been processed. - var html = _markdownService.ToHtml(part.Markdown); + // The default Markdown option is to entity escape html + // so filters must be run after the markdown has been processed. + var html = _markdownService.ToHtml(part.Markdown); - // The liquid rendering is for backwards compatibility and can be removed in a future version. - if (!settings.SanitizeHtml) + // The liquid rendering is for backwards compatibility and can be removed in a future version. + if (!settings.SanitizeHtml) + { + var model = new MarkdownBodyPartViewModel() { - var model = new MarkdownBodyPartViewModel() - { - Markdown = part.Markdown, - Html = html, - MarkdownBodyPart = part, - ContentItem = part.ContentItem, - }; - - html = await _liquidTemplateManager.RenderStringAsync(html, _htmlEncoder, model, - new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); - } + Markdown = part.Markdown, + Html = html, + MarkdownBodyPart = part, + ContentItem = part.ContentItem, + }; - html = await _shortcodeService.ProcessAsync(html, - new Context - { - ["ContentItem"] = part.ContentItem, - ["TypePartDefinition"] = contentTypePartDefinition - }); + html = await _liquidTemplateManager.RenderStringAsync(html, _htmlEncoder, model, + new Dictionary() { ["ContentItem"] = new ObjectValue(model.ContentItem) }); + } - if (settings.SanitizeHtml) + html = await _shortcodeService.ProcessAsync(html, + new Context { - html = _htmlSanitizerService.Sanitize(html); - } + ["ContentItem"] = part.ContentItem, + ["TypePartDefinition"] = contentTypePartDefinition + }); - bodyAspect.Body = new HtmlString(html); - } - catch + if (settings.SanitizeHtml) { - bodyAspect.Body = HtmlString.Empty; + html = _htmlSanitizerService.Sanitize(html); } - }); - } + + bodyAspect.Body = new HtmlString(html); + } + catch + { + bodyAspect.Body = HtmlString.Empty; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownBodyPartIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownBodyPartIndexHandler.cs index 99a2cc3069c..9fb946e2116 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownBodyPartIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownBodyPartIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.Indexing; using OrchardCore.Markdown.Models; -namespace OrchardCore.Markdown.Indexing +namespace OrchardCore.Markdown.Indexing; + +public class MarkdownBodyPartIndexHandler : ContentPartIndexHandler { - public class MarkdownBodyPartIndexHandler : ContentPartIndexHandler + public override Task BuildIndexAsync(MarkdownBodyPart part, BuildPartIndexContext context) { - public override Task BuildIndexAsync(MarkdownBodyPart part, BuildPartIndexContext context) - { - var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, part.Markdown, options); - } + var options = context.Settings.ToOptions() | DocumentIndexOptions.Sanitize; - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, part.Markdown, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownFieldIndexHandler.cs index 29aec3bfc69..4a9feed6b87 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Indexing/MarkdownFieldIndexHandler.cs @@ -2,20 +2,19 @@ using OrchardCore.Indexing; using OrchardCore.Markdown.Fields; -namespace OrchardCore.Markdown.Indexing +namespace OrchardCore.Markdown.Indexing; + +public class MarkdownFieldIndexHandler : ContentFieldIndexHandler { - public class MarkdownFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(MarkdownField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(MarkdownField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, field.Markdown, options); - } + var options = context.Settings.ToOptions(); - return Task.CompletedTask; + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, field.Markdown, options); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Media/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Media/Startup.cs index 08c78b62965..0206842a947 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Media/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Media/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.Modules; -namespace OrchardCore.Markdown.Media +namespace OrchardCore.Markdown.Media; + +[RequireFeatures("OrchardCore.Media")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Media")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Migrations.cs index 354803d4c59..e81eff208e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Migrations.cs @@ -6,77 +6,76 @@ using OrchardCore.Markdown.Fields; using OrchardCore.Markdown.Settings; -namespace OrchardCore.Markdown +namespace OrchardCore.Markdown; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("MarkdownBodyPart", builder => builder - .Attachable() - .WithDescription("Provides a Markdown formatted body for your content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("MarkdownBodyPart", builder => builder + .Attachable() + .WithDescription("Provides a Markdown formatted body for your content item.")); - // Shortcut other migration steps on new content definition schemas. - return 4; - } + // Shortcut other migration steps on new content definition schemas. + return 4; + } - // Migrate FieldSettings. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Migrate FieldSettings. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _contentDefinitionManager.MigrateFieldSettingsAsync(); - return 2; - } + return 2; + } - // This code can be removed in a later version. - public async Task UpdateFrom2Async() + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. + foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) { - // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. - foreach (var contentType in await _contentDefinitionManager.LoadTypeDefinitionsAsync()) + if (contentType.Parts.Any(x => x.PartDefinition.Name == "MarkdownBodyPart")) { - if (contentType.Parts.Any(x => x.PartDefinition.Name == "MarkdownBodyPart")) + await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.WithPart("MarkdownBodyPart", part => { - await _contentDefinitionManager.AlterTypeDefinitionAsync(contentType.Name, x => x.WithPart("MarkdownBodyPart", part => - { - part.MergeSettings(x => x.SanitizeHtml = false); - })); - } + part.MergeSettings(x => x.SanitizeHtml = false); + })); } - - return 3; } - // This code can be removed in a later version. - public async Task UpdateFrom3Async() + return 3; + } + + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. + var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); + foreach (var partDefinition in partDefinitions) { - // For backwards compatibility with liquid filters we disable html sanitization on existing field definitions. - var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); - foreach (var partDefinition in partDefinitions) + if (partDefinition.Fields.Any(x => x.FieldDefinition.Name == "MarkdownField")) { - if (partDefinition.Fields.Any(x => x.FieldDefinition.Name == "MarkdownField")) + await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => { - await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => + foreach (var fieldDefinition in partDefinition.Fields.Where(x => x.FieldDefinition.Name == "MarkdownField")) { - foreach (var fieldDefinition in partDefinition.Fields.Where(x => x.FieldDefinition.Name == "MarkdownField")) + partBuilder.WithField(fieldDefinition.Name, fieldBuilder => { - partBuilder.WithField(fieldDefinition.Name, fieldBuilder => - { - fieldBuilder.MergeSettings(s => s.SanitizeHtml = false); - }); - } - }); - } + fieldBuilder.MergeSettings(s => s.SanitizeHtml = false); + }); + } + }); } - - return 4; } + + return 4; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Models/MarkdownBodyPart.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Models/MarkdownBodyPart.cs index c5511fca9b0..a51eb47145f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Models/MarkdownBodyPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Models/MarkdownBodyPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Markdown.Models +namespace OrchardCore.Markdown.Models; + +public class MarkdownBodyPart : ContentPart { - public class MarkdownBodyPart : ContentPart - { - public string Markdown { get; set; } - } + public string Markdown { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/MarkdownBodyMetaWeblogDriver.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/MarkdownBodyMetaWeblogDriver.cs index e4464812245..77811af0297 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/MarkdownBodyMetaWeblogDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/MarkdownBodyMetaWeblogDriver.cs @@ -4,27 +4,26 @@ using OrchardCore.XmlRpc; using OrchardCore.XmlRpc.Models; -namespace OrchardCore.Markdown.RemotePublishing +namespace OrchardCore.Markdown.RemotePublishing; + +public sealed class MarkdownBodyMetaWeblogDriver : MetaWeblogDriver { - public sealed class MarkdownBodyMetaWeblogDriver : MetaWeblogDriver + public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) { - public override void BuildPost(XRpcStruct rpcStruct, XmlRpcContext context, ContentItem contentItem) + var bodyPart = contentItem.As(); + if (bodyPart == null) { - var bodyPart = contentItem.As(); - if (bodyPart == null) - { - return; - } - - rpcStruct.Set("description", bodyPart.Markdown); + return; } - public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + rpcStruct.Set("description", bodyPart.Markdown); + } + + public override void EditPost(XRpcStruct rpcStruct, ContentItem contentItem) + { + if (contentItem.As() != null) { - if (contentItem.As() != null) - { - contentItem.Alter(x => x.Markdown = rpcStruct.Optional("description")); - } + contentItem.Alter(x => x.Markdown = rpcStruct.Optional("description")); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/Startup.cs index 5d76f2ca19e..693db8a588f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/RemotePublishing/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.MetaWeblog; using OrchardCore.Modules; -namespace OrchardCore.Markdown.RemotePublishing +namespace OrchardCore.Markdown.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/DefaultMarkdownService.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/DefaultMarkdownService.cs index c4de392f37d..7769b734cfb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/DefaultMarkdownService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/DefaultMarkdownService.cs @@ -1,25 +1,24 @@ using Markdig; using Microsoft.Extensions.Options; -namespace OrchardCore.Markdown.Services +namespace OrchardCore.Markdown.Services; + +public class DefaultMarkdownService : IMarkdownService { - public class DefaultMarkdownService : IMarkdownService - { - private readonly MarkdownPipeline _markdownPipeline; + private readonly MarkdownPipeline _markdownPipeline; - public DefaultMarkdownService(IOptions options) + public DefaultMarkdownService(IOptions options) + { + var builder = new MarkdownPipelineBuilder(); + foreach (var action in options.Value.Configure) { - var builder = new MarkdownPipelineBuilder(); - foreach (var action in options.Value.Configure) - { - action?.Invoke(builder); - } - _markdownPipeline = builder.Build(); + action?.Invoke(builder); } + _markdownPipeline = builder.Build(); + } - public string ToHtml(string markdown) - { - return Markdig.Markdown.ToHtml(markdown, _markdownPipeline); - } + public string ToHtml(string markdown) + { + return Markdig.Markdown.ToHtml(markdown, _markdownPipeline); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptions.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptions.cs index 4d2112e8803..4146784388e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptions.cs @@ -2,10 +2,9 @@ using System.Collections.Generic; using Markdig; -namespace OrchardCore.Markdown.Services +namespace OrchardCore.Markdown.Services; + +public class MarkdownPipelineOptions { - public class MarkdownPipelineOptions - { - public List> Configure { get; } = []; - } + public List> Configure { get; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptionsExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptionsExtensions.cs index 9accbfcbca5..633e7a690b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptionsExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Services/MarkdownPipelineOptionsExtensions.cs @@ -2,19 +2,18 @@ using Markdig; using OrchardCore.Markdown.Services; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class MarkdownPipelineOptionsExtensions { - public static class MarkdownPipelineOptionsExtensions + /// + /// Adds a configuration action to the markdown pipeline builder. + /// + public static void ConfigureMarkdownPipeline(this IServiceCollection services, Action action) { - /// - /// Adds a configuration action to the markdown pipeline builder. - /// - public static void ConfigureMarkdownPipeline(this IServiceCollection services, Action action) + services.Configure(o => { - services.Configure(o => - { - o.Configure.Add(action); - }); - } + o.Configure.Add(action); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettings.cs index 0526807029e..12d6d37c697 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettings.cs @@ -1,10 +1,9 @@ using System.ComponentModel; -namespace OrchardCore.Markdown.Settings +namespace OrchardCore.Markdown.Settings; + +public class MarkdownBodyPartSettings { - public class MarkdownBodyPartSettings - { - [DefaultValue(true)] - public bool SanitizeHtml { get; set; } = true; - } + [DefaultValue(true)] + public bool SanitizeHtml { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettingsDisplayDriver.cs index e7b9249ee28..18058a14b82 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownBodyPartSettingsDisplayDriver.cs @@ -6,32 +6,31 @@ using OrchardCore.Markdown.Models; using OrchardCore.Markdown.ViewModels; -namespace OrchardCore.Markdown.Settings +namespace OrchardCore.Markdown.Settings; + +public sealed class MarkdownBodyPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class MarkdownBodyPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("MarkdownBodyPartSettings_Edit", model => { - return Initialize("MarkdownBodyPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.SanitizeHtml = settings.SanitizeHtml; - }).Location("Content:20"); - } + model.SanitizeHtml = settings.SanitizeHtml; + }).Location("Content:20"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new MarkdownBodyPartSettingsViewModel(); - var settings = new MarkdownBodyPartSettings(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new MarkdownBodyPartSettingsViewModel(); + var settings = new MarkdownBodyPartSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SanitizeHtml = model.SanitizeHtml; + settings.SanitizeHtml = model.SanitizeHtml; - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettings.cs index edb8d0cd684..866f12e0fc5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettings.cs @@ -1,11 +1,10 @@ using System.ComponentModel; -namespace OrchardCore.Markdown.Settings +namespace OrchardCore.Markdown.Settings; + +public class MarkdownFieldSettings { - public class MarkdownFieldSettings - { - [DefaultValue(true)] - public bool SanitizeHtml { get; set; } = true; - public string Hint { get; set; } - } + [DefaultValue(true)] + public bool SanitizeHtml { get; set; } = true; + public string Hint { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettingsDriver.cs index 495d1e812f7..9d2d7661e85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Settings/MarkdownFieldSettingsDriver.cs @@ -6,34 +6,33 @@ using OrchardCore.Markdown.Fields; using OrchardCore.Markdown.ViewModels; -namespace OrchardCore.Markdown.Settings +namespace OrchardCore.Markdown.Settings; + +public sealed class MarkdownFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class MarkdownFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("MarkdownFieldSettings_Edit", model => { - return Initialize("MarkdownFieldSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var settings = partFieldDefinition.GetSettings(); - model.SanitizeHtml = settings.SanitizeHtml; - model.Hint = settings.Hint; - }).Location("Content:20"); - } + model.SanitizeHtml = settings.SanitizeHtml; + model.Hint = settings.Hint; + }).Location("Content:20"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new MarkdownFieldSettingsViewModel(); - var settings = new MarkdownFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new MarkdownFieldSettingsViewModel(); + var settings = new MarkdownFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SanitizeHtml = model.SanitizeHtml; - settings.Hint = model.Hint; + settings.SanitizeHtml = model.SanitizeHtml; + settings.Hint = model.Hint; - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/Startup.cs index 3ae4f4b0585..3ceb52d825d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/Startup.cs @@ -20,52 +20,51 @@ using OrchardCore.Markdown.ViewModels; using OrchardCore.Modules; -namespace OrchardCore.Markdown +namespace OrchardCore.Markdown; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - private const string DefaultMarkdownExtensions = "nohtml+advanced"; + private const string DefaultMarkdownExtensions = "nohtml+advanced"; - private readonly IShellConfiguration _shellConfiguration; + private readonly IShellConfiguration _shellConfiguration; - public Startup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + public Startup(IShellConfiguration shellConfiguration) + { + _shellConfiguration = shellConfiguration; + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }) - .AddLiquidFilter("markdownify"); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }) + .AddLiquidFilter("markdownify"); - // Markdown Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Markdown Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); - // Markdown Field - services.AddContentField() - .UseDisplayDriver(); + // Markdown Field + services.AddContentField() + .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddOptions(); - services.ConfigureMarkdownPipeline((pipeline) => - { - var extensions = _shellConfiguration.GetValue("OrchardCore_Markdown:Extensions", DefaultMarkdownExtensions); - pipeline.Configure(extensions); - }); + services.AddOptions(); + services.ConfigureMarkdownPipeline((pipeline) => + { + var extensions = _shellConfiguration.GetValue("OrchardCore_Markdown:Extensions", DefaultMarkdownExtensions); + pipeline.Configure(extensions); + }); - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/EditMarkdownFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/EditMarkdownFieldViewModel.cs index c8d8221133b..089f8e49ca8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/EditMarkdownFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/EditMarkdownFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Markdown.Fields; -namespace OrchardCore.Markdown.ViewModels +namespace OrchardCore.Markdown.ViewModels; + +public class EditMarkdownFieldViewModel { - public class EditMarkdownFieldViewModel - { - public string Markdown { get; set; } - public MarkdownField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Markdown { get; set; } + public MarkdownField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartSettingsViewModel.cs index fc866f81d1a..9fa3bdb6ddc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Markdown.ViewModels +namespace OrchardCore.Markdown.ViewModels; + +public class MarkdownBodyPartSettingsViewModel { - public class MarkdownBodyPartSettingsViewModel - { - public bool SanitizeHtml { get; set; } - } + public bool SanitizeHtml { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartViewModel.cs index b50b5d2e320..0cc0e4cd9ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownBodyPartViewModel.cs @@ -3,20 +3,19 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Markdown.Models; -namespace OrchardCore.Markdown.ViewModels +namespace OrchardCore.Markdown.ViewModels; + +public class MarkdownBodyPartViewModel { - public class MarkdownBodyPartViewModel - { - public string Markdown { get; set; } - public string Html { get; set; } + public string Markdown { get; set; } + public string Html { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public MarkdownBodyPart MarkdownBodyPart { get; set; } + [BindNever] + public MarkdownBodyPart MarkdownBodyPart { get; set; } - [BindNever] - public ContentTypePartDefinition TypePartDefinition { get; set; } - } + [BindNever] + public ContentTypePartDefinition TypePartDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldSettingsViewModel.cs index bc19431087d..3019810777a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldSettingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Markdown.ViewModels +namespace OrchardCore.Markdown.ViewModels; + +public class MarkdownFieldSettingsViewModel { - public class MarkdownFieldSettingsViewModel - { - public bool SanitizeHtml { get; set; } - public string Hint { get; set; } - } + public bool SanitizeHtml { get; set; } + public string Hint { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldViewModel.cs index 09f1f82aaec..6e6534a7f42 100644 --- a/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Markdown/ViewModels/MarkdownFieldViewModel.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Markdown.Fields; -namespace OrchardCore.Markdown.ViewModels +namespace OrchardCore.Markdown.ViewModels; + +public class MarkdownFieldViewModel { - public class MarkdownFieldViewModel - { - public string Markdown { get; set; } - public string Html { get; set; } - public MarkdownField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string Markdown { get; set; } + public string Html { get; set; } + public MarkdownField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/AdminMenu.cs index c09cb5537cc..47810e8f486 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/AdminMenu.cs @@ -2,35 +2,34 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Media.AmazonS3 +namespace OrchardCore.Media.AmazonS3; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder.Add(S["Configuration"], configuration => configuration - .Add(S["Media"], S["Media"].PrefixPosition(), media => media - .Add(S["Amazon S3 Options"], S["Amazon S3 Options"].PrefixPosition(), options => options - .Action("Options", "Admin", "OrchardCore.Media.AmazonS3") - .Permission(Permissions.ViewAmazonS3MediaOptions) - .LocalNav() - ) + builder.Add(S["Configuration"], configuration => configuration + .Add(S["Media"], S["Media"].PrefixPosition(), media => media + .Add(S["Amazon S3 Options"], S["Amazon S3 Options"].PrefixPosition(), options => options + .Action("Options", "Admin", "OrchardCore.Media.AmazonS3") + .Permission(Permissions.ViewAmazonS3MediaOptions) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Controllers/AdminController.cs index 3db2145da07..67bf472f9d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Controllers/AdminController.cs @@ -8,47 +8,46 @@ using OrchardCore.FileStorage.AmazonS3; using OrchardCore.Media.AmazonS3.ViewModels; -namespace OrchardCore.Media.AmazonS3 +namespace OrchardCore.Media.AmazonS3; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly AwsStorageOptions _options; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public AdminController( + IAuthorizationService authorizationService, + IOptions options, + INotifier notifier, + IHtmlLocalizer htmlLocalizer) { - private readonly IAuthorizationService _authorizationService; - private readonly AwsStorageOptions _options; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; + _authorizationService = authorizationService; + _notifier = notifier; + H = htmlLocalizer; + _options = options.Value; + } - public AdminController( - IAuthorizationService authorizationService, - IOptions options, - INotifier notifier, - IHtmlLocalizer htmlLocalizer) + public async Task Options() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewAmazonS3MediaOptions)) { - _authorizationService = authorizationService; - _notifier = notifier; - H = htmlLocalizer; - _options = options.Value; + return Forbid(); } - public async Task Options() + if (_options.Validate().Any()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewAmazonS3MediaOptions)) - { - return Forbid(); - } - - if (_options.Validate().Any()) - { - await _notifier.ErrorAsync(H["The Amazon S3 Media feature is enabled, but it was not configured with appsettings.json."]); - } + await _notifier.ErrorAsync(H["The Amazon S3 Media feature is enabled, but it was not configured with appsettings.json."]); + } - var model = new OptionsViewModel - { - BucketName = _options.BucketName, - BasePath = _options.BasePath, - CreateBucket = _options.CreateBucket - }; + var model = new OptionsViewModel + { + BucketName = _options.BucketName, + BasePath = _options.BasePath, + CreateBucket = _options.CreateBucket + }; - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/ViewModels/OptionsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/ViewModels/OptionsViewModel.cs index 002bcefbdbc..ccb0bcb571c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/ViewModels/OptionsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/ViewModels/OptionsViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Media.AmazonS3.ViewModels +namespace OrchardCore.Media.AmazonS3.ViewModels; + +public class OptionsViewModel { - public class OptionsViewModel - { - public string BucketName { get; set; } + public string BucketName { get; set; } - public string BasePath { get; set; } + public string BasePath { get; set; } - public bool CreateBucket { get; set; } - } + public bool CreateBucket { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Azure/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Media.Azure/AdminMenu.cs index 2adb0d8efa8..9207949566f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Azure/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Azure/AdminMenu.cs @@ -2,35 +2,34 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Media.Azure +namespace OrchardCore.Media.Azure; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder.Add(S["Configuration"], configuration => configuration - .Add(S["Media"], S["Media"].PrefixPosition(), media => media - .Add(S["Azure Blob Options"], S["Azure Blob Options"].PrefixPosition(), options => options - .Action("Options", "Admin", "OrchardCore.Media.Azure") - .Permission(Permissions.ViewAzureMediaOptions) - .LocalNav() - ) + builder.Add(S["Configuration"], configuration => configuration + .Add(S["Media"], S["Media"].PrefixPosition(), media => media + .Add(S["Azure Blob Options"], S["Azure Blob Options"].PrefixPosition(), options => options + .Action("Options", "Admin", "OrchardCore.Media.Azure") + .Permission(Permissions.ViewAzureMediaOptions) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Azure/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Media.Azure/Controllers/AdminController.cs index 4a641df0d2c..d1d6d55c9c5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Azure/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Azure/Controllers/AdminController.cs @@ -6,39 +6,38 @@ using OrchardCore.Media.Azure.ViewModels; using OrchardCore.Modules; -namespace OrchardCore.Media.Azure +namespace OrchardCore.Media.Azure; + +[Feature("OrchardCore.Media.Azure.Storage")] +[Admin("MediaAzureBlob/{action}", "AzureBlob.{action}")] +public class AdminController : Controller { - [Feature("OrchardCore.Media.Azure.Storage")] - [Admin("MediaAzureBlob/{action}", "AzureBlob.{action}")] - public class AdminController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly MediaBlobStorageOptions _options; + + public AdminController( + IAuthorizationService authorizationService, + IOptions options) { - private readonly IAuthorizationService _authorizationService; - private readonly MediaBlobStorageOptions _options; + _authorizationService = authorizationService; + _options = options.Value; + } - public AdminController( - IAuthorizationService authorizationService, - IOptions options) + public async Task Options() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewAzureMediaOptions)) { - _authorizationService = authorizationService; - _options = options.Value; + return Forbid(); } - public async Task Options() + var model = new OptionsViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewAzureMediaOptions)) - { - return Forbid(); - } - - var model = new OptionsViewModel - { - CreateContainer = _options.CreateContainer, - ContainerName = _options.ContainerName, - ConnectionString = _options.ConnectionString, - BasePath = _options.BasePath - }; + CreateContainer = _options.CreateContainer, + ContainerName = _options.ContainerName, + ConnectionString = _options.ConnectionString, + BasePath = _options.BasePath + }; - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Azure/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Media.Azure/Startup.cs index e256ed82f0f..158ee60a2d3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Azure/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Azure/Startup.cs @@ -22,186 +22,185 @@ using SixLabors.ImageSharp.Web.Caching; using SixLabors.ImageSharp.Web.Caching.Azure; -namespace OrchardCore.Media.Azure +namespace OrchardCore.Media.Azure; + +[Feature("OrchardCore.Media.Azure.Storage")] +public sealed class Startup : Modules.StartupBase { - [Feature("OrchardCore.Media.Azure.Storage")] - public sealed class Startup : Modules.StartupBase - { - private readonly ILogger _logger; - private readonly IShellConfiguration _configuration; + private readonly ILogger _logger; + private readonly IShellConfiguration _configuration; - public Startup(ILogger logger, IShellConfiguration configuration) - { - _logger = logger; - _configuration = configuration; - } + public Startup(ILogger logger, IShellConfiguration configuration) + { + _logger = logger; + _configuration = configuration; + } - public override int Order - => OrchardCoreConstants.ConfigureOrder.AzureMediaStorage; + public override int Order + => OrchardCoreConstants.ConfigureOrder.AzureMediaStorage; - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddTransient, MediaBlobStorageOptionsConfiguration>(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddTransient, MediaBlobStorageOptionsConfiguration>(); - // Only replace default implementation if options are valid. - var section = _configuration.GetSection("OrchardCore_Media_Azure"); - var connectionString = section.GetValue(nameof(MediaBlobStorageOptions.ConnectionString)); - var containerName = section.GetValue(nameof(MediaBlobStorageOptions.ContainerName)); + // Only replace default implementation if options are valid. + var section = _configuration.GetSection("OrchardCore_Media_Azure"); + var connectionString = section.GetValue(nameof(MediaBlobStorageOptions.ConnectionString)); + var containerName = section.GetValue(nameof(MediaBlobStorageOptions.ContainerName)); - if (CheckOptions(connectionString, containerName, _logger)) + if (CheckOptions(connectionString, containerName, _logger)) + { + // Register a media cache file provider. + services.AddSingleton(serviceProvider => { - // Register a media cache file provider. - services.AddSingleton(serviceProvider => - { - var hostingEnvironment = serviceProvider.GetRequiredService(); + var hostingEnvironment = serviceProvider.GetRequiredService(); - if (string.IsNullOrWhiteSpace(hostingEnvironment.WebRootPath)) - { - throw new MediaConfigurationException("The wwwroot folder for serving cache media files is missing."); - } + if (string.IsNullOrWhiteSpace(hostingEnvironment.WebRootPath)) + { + throw new MediaConfigurationException("The wwwroot folder for serving cache media files is missing."); + } - var mediaOptions = serviceProvider.GetRequiredService>().Value; - var shellOptions = serviceProvider.GetRequiredService>(); - var shellSettings = serviceProvider.GetRequiredService(); - var logger = serviceProvider.GetRequiredService>(); + var mediaOptions = serviceProvider.GetRequiredService>().Value; + var shellOptions = serviceProvider.GetRequiredService>(); + var shellSettings = serviceProvider.GetRequiredService(); + var logger = serviceProvider.GetRequiredService>(); - var mediaCachePath = GetMediaCachePath( - hostingEnvironment, shellSettings, DefaultMediaFileStoreCacheFileProvider.AssetsCachePath); + var mediaCachePath = GetMediaCachePath( + hostingEnvironment, shellSettings, DefaultMediaFileStoreCacheFileProvider.AssetsCachePath); - if (!Directory.Exists(mediaCachePath)) - { - Directory.CreateDirectory(mediaCachePath); - } + if (!Directory.Exists(mediaCachePath)) + { + Directory.CreateDirectory(mediaCachePath); + } - return new DefaultMediaFileStoreCacheFileProvider(logger, mediaOptions.AssetsRequestPath, mediaCachePath); - }); + return new DefaultMediaFileStoreCacheFileProvider(logger, mediaOptions.AssetsRequestPath, mediaCachePath); + }); - // Replace the default media file provider with the media cache file provider. - services.Replace(ServiceDescriptor.Singleton(serviceProvider => - serviceProvider.GetRequiredService())); + // Replace the default media file provider with the media cache file provider. + services.Replace(ServiceDescriptor.Singleton(serviceProvider => + serviceProvider.GetRequiredService())); - // Register the media cache file provider as a file store cache provider. - services.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService()); + // Register the media cache file provider as a file store cache provider. + services.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService()); - // Replace the default media file store with a blob file store. - services.Replace(ServiceDescriptor.Singleton(serviceProvider => + // Replace the default media file store with a blob file store. + services.Replace(ServiceDescriptor.Singleton(serviceProvider => + { + var blobStorageOptions = serviceProvider.GetRequiredService>().Value; + var shellOptions = serviceProvider.GetRequiredService>(); + var shellSettings = serviceProvider.GetRequiredService(); + var mediaOptions = serviceProvider.GetRequiredService>().Value; + var clock = serviceProvider.GetRequiredService(); + var contentTypeProvider = serviceProvider.GetRequiredService(); + var mediaEventHandlers = serviceProvider.GetServices(); + var mediaCreatingEventHandlers = serviceProvider.GetServices(); + var logger = serviceProvider.GetRequiredService>(); + + var fileStore = new BlobFileStore(blobStorageOptions, clock, contentTypeProvider); + var mediaUrlBase = "/" + fileStore.Combine(shellSettings.RequestUrlPrefix, mediaOptions.AssetsRequestPath); + + var originalPathBase = serviceProvider.GetRequiredService().HttpContext + ?.Features.Get() + ?.OriginalPathBase ?? PathString.Empty; + + if (originalPathBase.HasValue) { - var blobStorageOptions = serviceProvider.GetRequiredService>().Value; - var shellOptions = serviceProvider.GetRequiredService>(); - var shellSettings = serviceProvider.GetRequiredService(); - var mediaOptions = serviceProvider.GetRequiredService>().Value; - var clock = serviceProvider.GetRequiredService(); - var contentTypeProvider = serviceProvider.GetRequiredService(); - var mediaEventHandlers = serviceProvider.GetServices(); - var mediaCreatingEventHandlers = serviceProvider.GetServices(); - var logger = serviceProvider.GetRequiredService>(); - - var fileStore = new BlobFileStore(blobStorageOptions, clock, contentTypeProvider); - var mediaUrlBase = "/" + fileStore.Combine(shellSettings.RequestUrlPrefix, mediaOptions.AssetsRequestPath); - - var originalPathBase = serviceProvider.GetRequiredService().HttpContext - ?.Features.Get() - ?.OriginalPathBase ?? PathString.Empty; - - if (originalPathBase.HasValue) - { - mediaUrlBase = fileStore.Combine(originalPathBase.Value, mediaUrlBase); - } - - return new DefaultMediaFileStore(fileStore, mediaUrlBase, mediaOptions.CdnBaseUrl, mediaEventHandlers, mediaCreatingEventHandlers, logger); - })); - - services.AddSingleton(); - - services.AddScoped(); - } - } + mediaUrlBase = fileStore.Combine(originalPathBase.Value, mediaUrlBase); + } - private static string GetMediaCachePath(IWebHostEnvironment hostingEnvironment, ShellSettings shellSettings, string assetsPath) - => PathExtensions.Combine(hostingEnvironment.WebRootPath, shellSettings.Name, assetsPath); + return new DefaultMediaFileStore(fileStore, mediaUrlBase, mediaOptions.CdnBaseUrl, mediaEventHandlers, mediaCreatingEventHandlers, logger); + })); - private static bool CheckOptions(string connectionString, string containerName, ILogger logger) - { - var optionsAreValid = true; - - if (string.IsNullOrWhiteSpace(connectionString)) - { - logger.LogError("Azure Media Storage is enabled but not active because the 'ConnectionString' is missing or empty in application configuration."); - optionsAreValid = false; - } + services.AddSingleton(); - if (string.IsNullOrWhiteSpace(containerName)) - { - logger.LogError("Azure Media Storage is enabled but not active because the 'ContainerName' is missing or empty in application configuration."); - optionsAreValid = false; - } - - return optionsAreValid; + services.AddScoped(); } } - [Feature("OrchardCore.Media.Azure.ImageSharpImageCache")] - public sealed class ImageSharpAzureBlobCacheStartup : Modules.StartupBase + private static string GetMediaCachePath(IWebHostEnvironment hostingEnvironment, ShellSettings shellSettings, string assetsPath) + => PathExtensions.Combine(hostingEnvironment.WebRootPath, shellSettings.Name, assetsPath); + + private static bool CheckOptions(string connectionString, string containerName, ILogger logger) { - private readonly IShellConfiguration _configuration; - private readonly ILogger _logger; + var optionsAreValid = true; - public ImageSharpAzureBlobCacheStartup( - IShellConfiguration configuration, - ILogger logger) + if (string.IsNullOrWhiteSpace(connectionString)) { - _configuration = configuration; - _logger = logger; + logger.LogError("Azure Media Storage is enabled but not active because the 'ConnectionString' is missing or empty in application configuration."); + optionsAreValid = false; } - public override int Order - => OrchardCoreConstants.ConfigureOrder.AzureImageSharpCache; - - public override void ConfigureServices(IServiceCollection services) + if (string.IsNullOrWhiteSpace(containerName)) { - services.AddTransient, ImageSharpBlobImageCacheOptionsConfiguration>(); - services.AddTransient, AzureBlobStorageCacheOptionsConfiguration>(); + logger.LogError("Azure Media Storage is enabled but not active because the 'ContainerName' is missing or empty in application configuration."); + optionsAreValid = false; + } - // Only replace default implementation if options are valid. - var section = _configuration.GetSection("OrchardCore_Media_Azure_ImageSharp_Cache"); - var connectionString = section.GetValue(nameof(MediaBlobStorageOptions.ConnectionString)); - var containerName = section.GetValue(nameof(MediaBlobStorageOptions.ContainerName)); + return optionsAreValid; + } +} - if (!CheckOptions(connectionString, containerName)) - { - return; - } +[Feature("OrchardCore.Media.Azure.ImageSharpImageCache")] +public sealed class ImageSharpAzureBlobCacheStartup : Modules.StartupBase +{ + private readonly IShellConfiguration _configuration; + private readonly ILogger _logger; - // Following https://docs.sixlabors.com/articles/imagesharp.web/imagecaches.html we'd use - // SetCache() but that's only available on IImageSharpBuilder after AddImageSharp(), - // what happens in OrchardCore.Media. Thus, an explicit Replace() is necessary. - services.Replace(ServiceDescriptor.Singleton()); + public ImageSharpAzureBlobCacheStartup( + IShellConfiguration configuration, + ILogger logger) + { + _configuration = configuration; + _logger = logger; + } - services.AddScoped(); - } + public override int Order + => OrchardCoreConstants.ConfigureOrder.AzureImageSharpCache; + + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient, ImageSharpBlobImageCacheOptionsConfiguration>(); + services.AddTransient, AzureBlobStorageCacheOptionsConfiguration>(); - private bool CheckOptions(string connectionString, string containerName) + // Only replace default implementation if options are valid. + var section = _configuration.GetSection("OrchardCore_Media_Azure_ImageSharp_Cache"); + var connectionString = section.GetValue(nameof(MediaBlobStorageOptions.ConnectionString)); + var containerName = section.GetValue(nameof(MediaBlobStorageOptions.ContainerName)); + + if (!CheckOptions(connectionString, containerName)) { - var optionsAreValid = true; + return; + } - if (string.IsNullOrWhiteSpace(connectionString)) - { - _logger.LogError( - "Azure Media ImageSharp Image Cache is enabled but not active because the 'ConnectionString' is missing or empty in application configuration."); - optionsAreValid = false; - } + // Following https://docs.sixlabors.com/articles/imagesharp.web/imagecaches.html we'd use + // SetCache() but that's only available on IImageSharpBuilder after AddImageSharp(), + // what happens in OrchardCore.Media. Thus, an explicit Replace() is necessary. + services.Replace(ServiceDescriptor.Singleton()); - if (string.IsNullOrWhiteSpace(containerName)) - { - _logger.LogError( - "Azure Media ImageSharp Image Cache is enabled but not active because the 'ContainerName' is missing or empty in application configuration."); - optionsAreValid = false; - } + services.AddScoped(); + } + + private bool CheckOptions(string connectionString, string containerName) + { + var optionsAreValid = true; - return optionsAreValid; + if (string.IsNullOrWhiteSpace(connectionString)) + { + _logger.LogError( + "Azure Media ImageSharp Image Cache is enabled but not active because the 'ConnectionString' is missing or empty in application configuration."); + optionsAreValid = false; } + + if (string.IsNullOrWhiteSpace(containerName)) + { + _logger.LogError( + "Azure Media ImageSharp Image Cache is enabled but not active because the 'ContainerName' is missing or empty in application configuration."); + optionsAreValid = false; + } + + return optionsAreValid; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Azure/ViewModels/OptionsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media.Azure/ViewModels/OptionsViewModel.cs index 58aa004c624..d634bb293ac 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Azure/ViewModels/OptionsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Azure/ViewModels/OptionsViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Media.Azure.ViewModels +namespace OrchardCore.Media.Azure.ViewModels; + +public class OptionsViewModel { - public class OptionsViewModel - { - public string ConnectionString { get; set; } - public string ContainerName { get; set; } - public string BasePath { get; set; } - public bool CreateContainer { get; set; } - } + public string ConnectionString { get; set; } + public string ContainerName { get; set; } + public string BasePath { get; set; } + public bool CreateContainer { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs index 67449f9bbbd..7654b759aaa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs @@ -3,45 +3,44 @@ using Cysharp.Text; using UglyToad.PdfPig; -namespace OrchardCore.Media.Indexing +namespace OrchardCore.Media.Indexing; + +public class PdfMediaFileTextProvider : IMediaFileTextProvider { - public class PdfMediaFileTextProvider : IMediaFileTextProvider + public async Task GetTextAsync(string path, Stream fileStream) { - public async Task GetTextAsync(string path, Stream fileStream) + // PdfPig requires the stream to be seekable, see: + // https://github.com/UglyToad/PdfPig/blob/master/src/UglyToad.PdfPig.Core/StreamInputBytes.cs#L45. + // Thus if it isn't, which is the case with e.g. Azure Blob Storage, we need to copy it to a new, seekable + // Stream. + MemoryStream seekableStream = null; + try { - // PdfPig requires the stream to be seekable, see: - // https://github.com/UglyToad/PdfPig/blob/master/src/UglyToad.PdfPig.Core/StreamInputBytes.cs#L45. - // Thus if it isn't, which is the case with e.g. Azure Blob Storage, we need to copy it to a new, seekable - // Stream. - MemoryStream seekableStream = null; - try + if (!fileStream.CanSeek) { - if (!fileStream.CanSeek) - { - // Since fileStream.Length might not be supported either, we can't preconfigure the capacity of the - // MemoryStream. - seekableStream = new MemoryStream(); - // While this involves loading the file into memory, we don't really have a choice. - await fileStream.CopyToAsync(seekableStream); - seekableStream.Position = 0; - } - - using var document = PdfDocument.Open(seekableStream ?? fileStream); - using var stringBuilder = ZString.CreateStringBuilder(); + // Since fileStream.Length might not be supported either, we can't preconfigure the capacity of the + // MemoryStream. + seekableStream = new MemoryStream(); + // While this involves loading the file into memory, we don't really have a choice. + await fileStream.CopyToAsync(seekableStream); + seekableStream.Position = 0; + } - foreach (var page in document.GetPages()) - { - stringBuilder.Append(page.Text); - } + using var document = PdfDocument.Open(seekableStream ?? fileStream); + using var stringBuilder = ZString.CreateStringBuilder(); - return stringBuilder.ToString(); + foreach (var page in document.GetPages()) + { + stringBuilder.Append(page.Text); } - finally + + return stringBuilder.ToString(); + } + finally + { + if (seekableStream != null) { - if (seekableStream != null) - { - await seekableStream.DisposeAsync(); - } + await seekableStream.DisposeAsync(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Media/AdminMenu.cs index a2b3525ad38..7af2d2eead1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/AdminMenu.cs @@ -2,79 +2,78 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Media +namespace OrchardCore.Media; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Content"], content => content - .AddClass("media") - .Id("media") - .Add(S["Media Library"], S["Media Library"].PrefixPosition(), media => media - .Permission(Permissions.ManageMedia) - .Action("Index", "Admin", "OrchardCore.Media") - .LocalNav() - ) - ); - - builder.Add(S["Configuration"], configuration => configuration - .Add(S["Media"], S["Media"].PrefixPosition(), media => media - .Add(S["Media Options"], S["Media Options"].PrefixPosition(), options => options - .Action("Options", "Admin", "OrchardCore.Media") - .Permission(Permissions.ViewMediaOptions) - .LocalNav() - ) - .Add(S["Media Profiles"], S["Media Profiles"].PrefixPosition(), mediaProfiles => mediaProfiles - .Action("Index", "MediaProfiles", "OrchardCore.Media") - .Permission(Permissions.ManageMediaProfiles) - .LocalNav() - ) + builder + .Add(S["Content"], content => content + .AddClass("media") + .Id("media") + .Add(S["Media Library"], S["Media Library"].PrefixPosition(), media => media + .Permission(Permissions.ManageMedia) + .Action("Index", "Admin", "OrchardCore.Media") + .LocalNav() ) ); - return Task.CompletedTask; - } + builder.Add(S["Configuration"], configuration => configuration + .Add(S["Media"], S["Media"].PrefixPosition(), media => media + .Add(S["Media Options"], S["Media Options"].PrefixPosition(), options => options + .Action("Options", "Admin", "OrchardCore.Media") + .Permission(Permissions.ViewMediaOptions) + .LocalNav() + ) + .Add(S["Media Profiles"], S["Media Profiles"].PrefixPosition(), mediaProfiles => mediaProfiles + .Action("Index", "MediaProfiles", "OrchardCore.Media") + .Permission(Permissions.ManageMediaProfiles) + .LocalNav() + ) + ) + ); + + return Task.CompletedTask; } +} + +public sealed class MediaCacheAdminMenu : INavigationProvider +{ + internal readonly IStringLocalizer S; - public sealed class MediaCacheAdminMenu : INavigationProvider + public MediaCacheAdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public MediaCacheAdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder.Add(S["Configuration"], configuration => configuration - .Add(S["Media"], S["Media"].PrefixPosition(), media => media - .Add(S["Media Cache"], S["Media Cache"].PrefixPosition(), cache => cache - .Action("Index", "MediaCache", "OrchardCore.Media") - .Permission(MediaCachePermissions.ManageAssetCache) - .LocalNav()) - )); + builder.Add(S["Configuration"], configuration => configuration + .Add(S["Media"], S["Media"].PrefixPosition(), media => media + .Add(S["Media Cache"], S["Media Cache"].PrefixPosition(), cache => cache + .Action("Index", "MediaCache", "OrchardCore.Media") + .Permission(MediaCachePermissions.ManageAssetCache) + .LocalNav()) + )); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/AdminController.cs index 7286ca87b59..4a9eece8c9b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/AdminController.cs @@ -17,567 +17,566 @@ using OrchardCore.Media.Services; using OrchardCore.Media.ViewModels; -namespace OrchardCore.Media.Controllers +namespace OrchardCore.Media.Controllers; + +[Admin("Media/{action}", "Media.{action}")] +public class AdminController : Controller { - [Admin("Media/{action}", "Media.{action}")] - public class AdminController : Controller + private static readonly char[] _invalidFolderNameCharacters = ['\\', '/']; + private static readonly char[] _extensionSeperator = [' ', ',']; + + private readonly IMediaFileStore _mediaFileStore; + private readonly IMediaNameNormalizerService _mediaNameNormalizerService; + private readonly IAuthorizationService _authorizationService; + private readonly IContentTypeProvider _contentTypeProvider; + private readonly ILogger _logger; + protected readonly IStringLocalizer S; + private readonly MediaOptions _mediaOptions; + private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; + private readonly IChunkFileUploadService _chunkFileUploadService; + private readonly IFileVersionProvider _fileVersionProvider; + private readonly IServiceProvider _serviceProvider; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + + public AdminController( + IMediaFileStore mediaFileStore, + IMediaNameNormalizerService mediaNameNormalizerService, + IAuthorizationService authorizationService, + IContentTypeProvider contentTypeProvider, + IOptions options, + ILogger logger, + IStringLocalizer stringLocalizer, + IUserAssetFolderNameProvider userAssetFolderNameProvider, + IChunkFileUploadService chunkFileUploadService, + IFileVersionProvider fileVersionProvider, + IServiceProvider serviceProvider, + AttachedMediaFieldFileService attachedMediaFieldFileService) { - private static readonly char[] _invalidFolderNameCharacters = ['\\', '/']; - private static readonly char[] _extensionSeperator = [' ', ',']; - - private readonly IMediaFileStore _mediaFileStore; - private readonly IMediaNameNormalizerService _mediaNameNormalizerService; - private readonly IAuthorizationService _authorizationService; - private readonly IContentTypeProvider _contentTypeProvider; - private readonly ILogger _logger; - protected readonly IStringLocalizer S; - private readonly MediaOptions _mediaOptions; - private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; - private readonly IChunkFileUploadService _chunkFileUploadService; - private readonly IFileVersionProvider _fileVersionProvider; - private readonly IServiceProvider _serviceProvider; - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - - public AdminController( - IMediaFileStore mediaFileStore, - IMediaNameNormalizerService mediaNameNormalizerService, - IAuthorizationService authorizationService, - IContentTypeProvider contentTypeProvider, - IOptions options, - ILogger logger, - IStringLocalizer stringLocalizer, - IUserAssetFolderNameProvider userAssetFolderNameProvider, - IChunkFileUploadService chunkFileUploadService, - IFileVersionProvider fileVersionProvider, - IServiceProvider serviceProvider, - AttachedMediaFieldFileService attachedMediaFieldFileService) - { - _mediaFileStore = mediaFileStore; - _mediaNameNormalizerService = mediaNameNormalizerService; - _authorizationService = authorizationService; - _contentTypeProvider = contentTypeProvider; - _mediaOptions = options.Value; - _logger = logger; - S = stringLocalizer; - _userAssetFolderNameProvider = userAssetFolderNameProvider; - _chunkFileUploadService = chunkFileUploadService; - _fileVersionProvider = fileVersionProvider; - _serviceProvider = serviceProvider; - _attachedMediaFieldFileService = attachedMediaFieldFileService; - } - - [Admin("Media", "Media.Index")] - public async Task Index() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) - { - return Forbid(); - } + _mediaFileStore = mediaFileStore; + _mediaNameNormalizerService = mediaNameNormalizerService; + _authorizationService = authorizationService; + _contentTypeProvider = contentTypeProvider; + _mediaOptions = options.Value; + _logger = logger; + S = stringLocalizer; + _userAssetFolderNameProvider = userAssetFolderNameProvider; + _chunkFileUploadService = chunkFileUploadService; + _fileVersionProvider = fileVersionProvider; + _serviceProvider = serviceProvider; + _attachedMediaFieldFileService = attachedMediaFieldFileService; + } - return View(); + [Admin("Media", "Media.Index")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) + { + return Forbid(); } - public async Task>> GetFolders(string path) + return View(); + } + + public async Task>> GetFolders(string path) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) - { - return Forbid(); - } + return Forbid(); + } - if (string.IsNullOrEmpty(path)) - { - path = string.Empty; - } + if (string.IsNullOrEmpty(path)) + { + path = string.Empty; + } - if (await _mediaFileStore.GetDirectoryInfoAsync(path) == null) - { - return NotFound(); - } + if (await _mediaFileStore.GetDirectoryInfoAsync(path) == null) + { + return NotFound(); + } - // create default folders if not exist - if (await _authorizationService.AuthorizeAsync(User, Permissions.ManageOwnMedia) - && await _mediaFileStore.GetDirectoryInfoAsync(_mediaFileStore.Combine(_mediaOptions.AssetsUsersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(User))) == null) - { - await _mediaFileStore.TryCreateDirectoryAsync(_mediaFileStore.Combine(_mediaOptions.AssetsUsersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(User))); - } + // create default folders if not exist + if (await _authorizationService.AuthorizeAsync(User, Permissions.ManageOwnMedia) + && await _mediaFileStore.GetDirectoryInfoAsync(_mediaFileStore.Combine(_mediaOptions.AssetsUsersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(User))) == null) + { + await _mediaFileStore.TryCreateDirectoryAsync(_mediaFileStore.Combine(_mediaOptions.AssetsUsersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(User))); + } - var allowed = _mediaFileStore.GetDirectoryContentAsync(path) - .WhereAwait(async e => e.IsDirectory && await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path)); + var allowed = _mediaFileStore.GetDirectoryContentAsync(path) + .WhereAwait(async e => e.IsDirectory && await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path)); - return Ok(await allowed.Select(folder => - { - var isSpecial = IsSpecialFolder(folder.Path); - return new MediaFolderViewModel() - { - Name = folder.Name, - Path = folder.Path, - DirectoryPath = folder.DirectoryPath, - IsDirectory = true, - LastModifiedUtc = folder.LastModifiedUtc, - Length = folder.Length, - CanCreateFolder = !isSpecial, - CanDeleteFolder = !isSpecial - }; - }).ToListAsync()); + return Ok(await allowed.Select(folder => + { + var isSpecial = IsSpecialFolder(folder.Path); + return new MediaFolderViewModel() + { + Name = folder.Name, + Path = folder.Path, + DirectoryPath = folder.DirectoryPath, + IsDirectory = true, + LastModifiedUtc = folder.LastModifiedUtc, + Length = folder.Length, + CanCreateFolder = !isSpecial, + CanDeleteFolder = !isSpecial + }; + }).ToListAsync()); + } + + public async Task>> GetMediaItems(string path, string extensions) + { + if (string.IsNullOrEmpty(path)) + { + path = string.Empty; } - public async Task>> GetMediaItems(string path, string extensions) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) { - if (string.IsNullOrEmpty(path)) - { - path = string.Empty; - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) - { - return Forbid(); - } + if (await _mediaFileStore.GetDirectoryInfoAsync(path) == null) + { + return NotFound(); + } - if (await _mediaFileStore.GetDirectoryInfoAsync(path) == null) - { - return NotFound(); - } + var allowedExtensions = GetRequestedExtensions(extensions, false); - var allowedExtensions = GetRequestedExtensions(extensions, false); + var allowed = _mediaFileStore.GetDirectoryContentAsync(path) + .WhereAwait(async e => + !e.IsDirectory + && (allowedExtensions.Count == 0 || allowedExtensions.Contains(Path.GetExtension(e.Path))) + && await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path)) + .Select(e => CreateFileResult(e)); - var allowed = _mediaFileStore.GetDirectoryContentAsync(path) - .WhereAwait(async e => - !e.IsDirectory - && (allowedExtensions.Count == 0 || allowedExtensions.Contains(Path.GetExtension(e.Path))) - && await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)e.Path)) - .Select(e => CreateFileResult(e)); + return Ok(await allowed.ToListAsync()); + } - return Ok(await allowed.ToListAsync()); + public async Task> GetMediaItem(string path) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || (HttpContext.IsSecureMediaEnabled() && !await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)(path ?? string.Empty)))) + { + return Forbid(); } - public async Task> GetMediaItem(string path) + if (string.IsNullOrEmpty(path)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || (HttpContext.IsSecureMediaEnabled() && !await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)(path ?? string.Empty)))) - { - return Forbid(); - } - - if (string.IsNullOrEmpty(path)) - { - return NotFound(); - } - - var fileEntry = await _mediaFileStore.GetFileInfoAsync(path); + return NotFound(); + } - if (fileEntry == null) - { - return NotFound(); - } + var fileEntry = await _mediaFileStore.GetFileInfoAsync(path); - return CreateFileResult(fileEntry); + if (fileEntry == null) + { + return NotFound(); } - [HttpPost] - [MediaSizeLimit] - public async Task Upload(string path, string extensions) + return CreateFileResult(fileEntry); + } + + [HttpPost] + [MediaSizeLimit] + public async Task Upload(string path, string extensions) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || (HttpContext.IsSecureMediaEnabled() && !await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)(path ?? string.Empty)))) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || (HttpContext.IsSecureMediaEnabled() && !await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)(path ?? string.Empty)))) - { - return Forbid(); - } + return Forbid(); + } - var allowedExtensions = GetRequestedExtensions(extensions, true); + var allowedExtensions = GetRequestedExtensions(extensions, true); - return await _chunkFileUploadService.ProcessRequestAsync( - Request, + return await _chunkFileUploadService.ProcessRequestAsync( + Request, - // We need this empty object because the frontend expects a JSON object in the response. - (_, _, _) => Task.FromResult(Ok(new { })), - async (files) => + // We need this empty object because the frontend expects a JSON object in the response. + (_, _, _) => Task.FromResult(Ok(new { })), + async (files) => + { + if (string.IsNullOrEmpty(path)) { - if (string.IsNullOrEmpty(path)) - { - path = string.Empty; - } + path = string.Empty; + } - var result = new List(); + var result = new List(); - // Loop through each file in the request. - foreach (var file in files) + // Loop through each file in the request. + foreach (var file in files) + { + var extension = Path.GetExtension(file.FileName); + + if (!allowedExtensions.Contains(extension)) { - var extension = Path.GetExtension(file.FileName); + result.Add(new + { + name = file.FileName, + size = file.Length, + folder = path, + error = S["This file extension is not allowed: {0}", extension].ToString() + }); - if (!allowedExtensions.Contains(extension)) + if (_logger.IsEnabled(LogLevel.Information)) { - result.Add(new - { - name = file.FileName, - size = file.Length, - folder = path, - error = S["This file extension is not allowed: {0}", extension].ToString() - }); - - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("File extension not allowed: '{File}'", file.FileName); - } - - continue; + _logger.LogInformation("File extension not allowed: '{File}'", file.FileName); } - var fileName = _mediaNameNormalizerService.NormalizeFileName(file.FileName); + continue; + } + + var fileName = _mediaNameNormalizerService.NormalizeFileName(file.FileName); + + Stream stream = null; + try + { + var mediaFilePath = _mediaFileStore.Combine(path, fileName); + stream = file.OpenReadStream(); + mediaFilePath = await _mediaFileStore.CreateFileFromStreamAsync(mediaFilePath, stream); + + var mediaFile = await _mediaFileStore.GetFileInfoAsync(mediaFilePath); - Stream stream = null; + // The .NET AWS SDK, and only that from the built-in ones (but others maybe too), disposes + // the stream. There's no better way to check for that than handling the exception. An + // alternative would be to re-read the file for every other storage provider as well but + // that would be wasteful. try { - var mediaFilePath = _mediaFileStore.Combine(path, fileName); - stream = file.OpenReadStream(); - mediaFilePath = await _mediaFileStore.CreateFileFromStreamAsync(mediaFilePath, stream); - - var mediaFile = await _mediaFileStore.GetFileInfoAsync(mediaFilePath); - - // The .NET AWS SDK, and only that from the built-in ones (but others maybe too), disposes - // the stream. There's no better way to check for that than handling the exception. An - // alternative would be to re-read the file for every other storage provider as well but - // that would be wasteful. - try - { - stream.Position = 0; - } - catch (ObjectDisposedException) - { - stream = null; - } - - await PreCacheRemoteMedia(mediaFile, stream); - - result.Add(CreateFileResult(mediaFile)); + stream.Position = 0; } - catch (Exception ex) + catch (ObjectDisposedException) { - _logger.LogError(ex, "An error occurred while uploading a media"); - - result.Add(new - { - name = fileName, - size = file.Length, - folder = path, - error = ex.Message - }); + stream = null; } - finally + + await PreCacheRemoteMedia(mediaFile, stream); + + result.Add(CreateFileResult(mediaFile)); + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occurred while uploading a media"); + + result.Add(new { - stream?.Dispose(); - } + name = fileName, + size = file.Length, + folder = path, + error = ex.Message + }); + } + finally + { + stream?.Dispose(); } + } + + return Ok(new { files = result.ToArray() }); + }); + } - return Ok(new { files = result.ToArray() }); - }); + [HttpPost] + public async Task DeleteFolder(string path) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) + { + return Forbid(); } - [HttpPost] - public async Task DeleteFolder(string path) + if (string.IsNullOrEmpty(path)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) - { - return Forbid(); - } + return StatusCode(StatusCodes.Status403Forbidden, S["Cannot delete root media folder"]); + } - if (string.IsNullOrEmpty(path)) - { - return StatusCode(StatusCodes.Status403Forbidden, S["Cannot delete root media folder"]); - } + var mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(path); + if (mediaFolder != null && !mediaFolder.IsDirectory) + { + return StatusCode(StatusCodes.Status403Forbidden, S["Cannot delete path because it is not a directory"]); + } - var mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(path); - if (mediaFolder != null && !mediaFolder.IsDirectory) - { - return StatusCode(StatusCodes.Status403Forbidden, S["Cannot delete path because it is not a directory"]); - } + if (await _mediaFileStore.TryDeleteDirectoryAsync(path) == false) + { + return NotFound(); + } - if (await _mediaFileStore.TryDeleteDirectoryAsync(path) == false) - { - return NotFound(); - } + return Ok(); + } - return Ok(); + [HttpPost] + public async Task DeleteMedia(string path) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) + { + return Forbid(); } - [HttpPost] - public async Task DeleteMedia(string path) + if (string.IsNullOrEmpty(path)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)path)) - { - return Forbid(); - } + return NotFound(); + } - if (string.IsNullOrEmpty(path)) - { - return NotFound(); - } + if (!await _mediaFileStore.TryDeleteFileAsync(path)) + { + return NotFound(); + } - if (!await _mediaFileStore.TryDeleteFileAsync(path)) - { - return NotFound(); - } + return Ok(); + } - return Ok(); + [HttpPost] + public async Task MoveMedia(string oldPath, string newPath) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)oldPath) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)newPath)) + { + return Forbid(); } - [HttpPost] - public async Task MoveMedia(string oldPath, string newPath) + if (string.IsNullOrEmpty(oldPath) || string.IsNullOrEmpty(newPath)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)oldPath) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)newPath)) - { - return Forbid(); - } + return NotFound(); + } - if (string.IsNullOrEmpty(oldPath) || string.IsNullOrEmpty(newPath)) - { - return NotFound(); - } + if (await _mediaFileStore.GetFileInfoAsync(oldPath) == null) + { + return NotFound(); + } - if (await _mediaFileStore.GetFileInfoAsync(oldPath) == null) - { - return NotFound(); - } + var newExtension = Path.GetExtension(newPath); - var newExtension = Path.GetExtension(newPath); + if (!_mediaOptions.AllowedFileExtensions.Contains(newExtension, StringComparer.OrdinalIgnoreCase)) + { + return BadRequest(S["This file extension is not allowed: {0}", newExtension]); + } - if (!_mediaOptions.AllowedFileExtensions.Contains(newExtension, StringComparer.OrdinalIgnoreCase)) - { - return BadRequest(S["This file extension is not allowed: {0}", newExtension]); - } + if (await _mediaFileStore.GetFileInfoAsync(newPath) != null) + { + return BadRequest(S["Cannot move media because a file already exists with the same name"]); + } - if (await _mediaFileStore.GetFileInfoAsync(newPath) != null) - { - return BadRequest(S["Cannot move media because a file already exists with the same name"]); - } + await _mediaFileStore.MoveFileAsync(oldPath, newPath); - await _mediaFileStore.MoveFileAsync(oldPath, newPath); + var newFileInfo = await _mediaFileStore.GetFileInfoAsync(newPath); + await PreCacheRemoteMedia(newFileInfo); - var newFileInfo = await _mediaFileStore.GetFileInfoAsync(newPath); - await PreCacheRemoteMedia(newFileInfo); + return Ok(new { newUrl = GetCacheBustingMediaPublicUrl(newPath) }); + } - return Ok(new { newUrl = GetCacheBustingMediaPublicUrl(newPath) }); + [HttpPost] + public async Task DeleteMediaList(string[] paths) + { + if (paths == null) + { + return NotFound(); } - [HttpPost] - public async Task DeleteMediaList(string[] paths) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) { - if (paths == null) - { - return NotFound(); - } - - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia)) - { - return Forbid(); - } - - foreach (var path in paths) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAttachedMediaFieldsFolder, (object)path)) - { - return Forbid(); - } - } - - foreach (var path in paths) - { - if (!await _mediaFileStore.TryDeleteFileAsync(path)) - { - return NotFound(); - } - } - - return Ok(); + return Forbid(); } - [HttpPost] - public async Task MoveMediaList(string[] mediaNames, string sourceFolder, string targetFolder) + foreach (var path in paths) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)sourceFolder) - || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)targetFolder)) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageAttachedMediaFieldsFolder, (object)path)) { return Forbid(); } + } - if ((mediaNames == null) || (mediaNames.Length < 1) - || string.IsNullOrEmpty(sourceFolder) - || string.IsNullOrEmpty(targetFolder)) + foreach (var path in paths) + { + if (!await _mediaFileStore.TryDeleteFileAsync(path)) { return NotFound(); } + } - sourceFolder = sourceFolder == "root" ? string.Empty : sourceFolder; - targetFolder = targetFolder == "root" ? string.Empty : targetFolder; + return Ok(); + } - var filesOnError = new List(); + [HttpPost] + public async Task MoveMediaList(string[] mediaNames, string sourceFolder, string targetFolder) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)sourceFolder) + || !await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)targetFolder)) + { + return Forbid(); + } - foreach (var name in mediaNames) - { - var sourcePath = _mediaFileStore.Combine(sourceFolder, name); - var targetPath = _mediaFileStore.Combine(targetFolder, name); - try - { - await _mediaFileStore.MoveFileAsync(sourcePath, targetPath); - } - catch (FileStoreException) - { - filesOnError.Add(sourcePath); - } - } + if ((mediaNames == null) || (mediaNames.Length < 1) + || string.IsNullOrEmpty(sourceFolder) + || string.IsNullOrEmpty(targetFolder)) + { + return NotFound(); + } - if (filesOnError.Count > 0) - { - return BadRequest(S["Error when moving files. Maybe they already exist on the target folder? Files on error: {0}", string.Join(",", filesOnError)].ToString()); - } + sourceFolder = sourceFolder == "root" ? string.Empty : sourceFolder; + targetFolder = targetFolder == "root" ? string.Empty : targetFolder; - return Ok(); - } + var filesOnError = new List(); - [HttpPost] - public async Task> CreateFolder( - string path, string name, - [FromServices] IAuthorizationService authorizationService) + foreach (var name in mediaNames) { - if (string.IsNullOrEmpty(path)) + var sourcePath = _mediaFileStore.Combine(sourceFolder, name); + var targetPath = _mediaFileStore.Combine(targetFolder, name); + try { - path = string.Empty; + await _mediaFileStore.MoveFileAsync(sourcePath, targetPath); } - - name = _mediaNameNormalizerService.NormalizeFolderName(name); - - if (_invalidFolderNameCharacters.Any(invalidChar => name.Contains(invalidChar))) + catch (FileStoreException) { - return BadRequest(S["Cannot create folder because the folder name contains invalid characters"]); + filesOnError.Add(sourcePath); } + } - var newPath = _mediaFileStore.Combine(path, name); + if (filesOnError.Count > 0) + { + return BadRequest(S["Error when moving files. Maybe they already exist on the target folder? Files on error: {0}", string.Join(",", filesOnError)].ToString()); + } - if (!await authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) - || !await authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)newPath)) - { - return Forbid(); - } + return Ok(); + } - var mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(newPath); - if (mediaFolder != null) - { - return BadRequest(S["Cannot create folder because a folder already exists with the same name"]); - } + [HttpPost] + public async Task> CreateFolder( + string path, string name, + [FromServices] IAuthorizationService authorizationService) + { + if (string.IsNullOrEmpty(path)) + { + path = string.Empty; + } - var existingFile = await _mediaFileStore.GetFileInfoAsync(newPath); - if (existingFile != null) - { - return BadRequest(S["Cannot create folder because a file already exists with the same name"]); - } + name = _mediaNameNormalizerService.NormalizeFolderName(name); - await _mediaFileStore.TryCreateDirectoryAsync(newPath); + if (_invalidFolderNameCharacters.Any(invalidChar => name.Contains(invalidChar))) + { + return BadRequest(S["Cannot create folder because the folder name contains invalid characters"]); + } - mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(newPath); + var newPath = _mediaFileStore.Combine(path, name); - return new ObjectResult(mediaFolder); + if (!await authorizationService.AuthorizeAsync(User, Permissions.ManageMedia) + || !await authorizationService.AuthorizeAsync(User, Permissions.ManageMediaFolder, (object)newPath)) + { + return Forbid(); } - public object CreateFileResult(IFileStoreEntry mediaFile) + var mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(newPath); + if (mediaFolder != null) { - _contentTypeProvider.TryGetContentType(mediaFile.Name, out var contentType); - - return new - { - name = mediaFile.Name, - size = mediaFile.Length, - lastModify = mediaFile.LastModifiedUtc.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds, - folder = mediaFile.DirectoryPath, - url = GetCacheBustingMediaPublicUrl(mediaFile.Path), - mediaPath = mediaFile.Path, - mime = contentType ?? "application/octet-stream", - mediaText = string.Empty, - anchor = new { x = 0.5f, y = 0.5f }, - attachedFileName = string.Empty - }; + return BadRequest(S["Cannot create folder because a folder already exists with the same name"]); } - public async Task MediaApplication(MediaApplicationViewModel model) + var existingFile = await _mediaFileStore.GetFileInfoAsync(newPath); + if (existingFile != null) { - // Check if the user has access to new folders. If not, we hide the "create folder" button from the root folder. - model.AllowNewRootFolders = !HttpContext.IsSecureMediaEnabled() || await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)"_non-existent-path-87FD1922-8F88-4A33-9766-DA03E6E6F7BA"); - - return View(model); + return BadRequest(S["Cannot create folder because a file already exists with the same name"]); } - public async Task Options() + await _mediaFileStore.TryCreateDirectoryAsync(newPath); + + mediaFolder = await _mediaFileStore.GetDirectoryInfoAsync(newPath); + + return new ObjectResult(mediaFolder); + } + + public object CreateFileResult(IFileStoreEntry mediaFile) + { + _contentTypeProvider.TryGetContentType(mediaFile.Name, out var contentType); + + return new { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewMediaOptions)) - { - return Forbid(); - } + name = mediaFile.Name, + size = mediaFile.Length, + lastModify = mediaFile.LastModifiedUtc.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds, + folder = mediaFile.DirectoryPath, + url = GetCacheBustingMediaPublicUrl(mediaFile.Path), + mediaPath = mediaFile.Path, + mime = contentType ?? "application/octet-stream", + mediaText = string.Empty, + anchor = new { x = 0.5f, y = 0.5f }, + attachedFileName = string.Empty + }; + } - return View(_mediaOptions); - } + public async Task MediaApplication(MediaApplicationViewModel model) + { + // Check if the user has access to new folders. If not, we hide the "create folder" button from the root folder. + model.AllowNewRootFolders = !HttpContext.IsSecureMediaEnabled() || await _authorizationService.AuthorizeAsync(User, SecureMediaPermissions.ViewMedia, (object)"_non-existent-path-87FD1922-8F88-4A33-9766-DA03E6E6F7BA"); + + return View(model); + } - private HashSet GetRequestedExtensions(string exts, bool fallback) + public async Task Options() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ViewMediaOptions)) { - if (!string.IsNullOrWhiteSpace(exts)) - { - var extensions = exts.Split(_extensionSeperator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + return Forbid(); + } - var requestedExtensions = _mediaOptions.AllowedFileExtensions - .Intersect(extensions) - .ToHashSet(StringComparer.OrdinalIgnoreCase); + return View(_mediaOptions); + } - if (requestedExtensions.Count > 0) - { - return requestedExtensions; - } - } + private HashSet GetRequestedExtensions(string exts, bool fallback) + { + if (!string.IsNullOrWhiteSpace(exts)) + { + var extensions = exts.Split(_extensionSeperator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + var requestedExtensions = _mediaOptions.AllowedFileExtensions + .Intersect(extensions) + .ToHashSet(StringComparer.OrdinalIgnoreCase); - if (fallback) + if (requestedExtensions.Count > 0) { - return _mediaOptions.AllowedFileExtensions - .ToHashSet(StringComparer.OrdinalIgnoreCase); + return requestedExtensions; } + } - return []; + if (fallback) + { + return _mediaOptions.AllowedFileExtensions + .ToHashSet(StringComparer.OrdinalIgnoreCase); } - private string GetCacheBustingMediaPublicUrl(string path) => - _fileVersionProvider.AddFileVersionToPath(HttpContext.Request.PathBase, _mediaFileStore.MapPathToPublicUrl(path)); + return []; + } - // If a remote storage is used, then we need to preemptively cache the newly uploaded or renamed file. Without - // this, the Media Library page will try to load the thumbnail without a cache busting parameter, since - // ShellFileVersionProvider won't find it in the local cache. - // This is not required for files moved across folders, because the folder will be reopened anyway. - private async Task PreCacheRemoteMedia(IFileStoreEntry mediaFile, Stream stream = null) - { - var mediaFileStoreCache = _serviceProvider.GetService(); - if (mediaFileStoreCache == null) - { - return; - } + private string GetCacheBustingMediaPublicUrl(string path) => + _fileVersionProvider.AddFileVersionToPath(HttpContext.Request.PathBase, _mediaFileStore.MapPathToPublicUrl(path)); - Stream localStream = null; + // If a remote storage is used, then we need to preemptively cache the newly uploaded or renamed file. Without + // this, the Media Library page will try to load the thumbnail without a cache busting parameter, since + // ShellFileVersionProvider won't find it in the local cache. + // This is not required for files moved across folders, because the folder will be reopened anyway. + private async Task PreCacheRemoteMedia(IFileStoreEntry mediaFile, Stream stream = null) + { + var mediaFileStoreCache = _serviceProvider.GetService(); + if (mediaFileStoreCache == null) + { + return; + } - if (stream == null) - { - stream = localStream = await _mediaFileStore.GetFileStreamAsync(mediaFile); - } + Stream localStream = null; - try - { - await mediaFileStoreCache.SetCacheAsync(stream, mediaFile, HttpContext.RequestAborted); - } - finally - { - localStream?.Dispose(); - } + if (stream == null) + { + stream = localStream = await _mediaFileStore.GetFileStreamAsync(mediaFile); } - private bool IsSpecialFolder(string path) - => string.Equals(path, _mediaOptions.AssetsUsersFolder, StringComparison.OrdinalIgnoreCase) || string.Equals(path, _attachedMediaFieldFileService.MediaFieldsFolder, StringComparison.OrdinalIgnoreCase); + try + { + await mediaFileStoreCache.SetCacheAsync(stream, mediaFile, HttpContext.RequestAborted); + } + finally + { + localStream?.Dispose(); + } } + + private bool IsSpecialFolder(string path) + => string.Equals(path, _mediaOptions.AssetsUsersFolder, StringComparison.OrdinalIgnoreCase) || string.Equals(path, _attachedMediaFieldFileService.MediaFieldsFolder, StringComparison.OrdinalIgnoreCase); } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaCacheController.cs b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaCacheController.cs index 2f7399159d5..eba9f0e28e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaCacheController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaCacheController.cs @@ -9,71 +9,70 @@ using OrchardCore.Media.ViewModels; using OrchardCore.Modules; -namespace OrchardCore.Media.Controllers +namespace OrchardCore.Media.Controllers; + +[Feature("OrchardCore.Media.Cache")] +[Admin("MediaCache/{action}", "MediaCache.{action}")] +public class MediaCacheController : Controller { - [Feature("OrchardCore.Media.Cache")] - [Admin("MediaCache/{action}", "MediaCache.{action}")] - public class MediaCacheController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IMediaFileStoreCache _mediaFileStoreCache; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public MediaCacheController( + IAuthorizationService authorizationService, + IServiceProvider serviceProvider, + INotifier notifier, + IHtmlLocalizer htmlLocalizer + ) { - private readonly IAuthorizationService _authorizationService; - private readonly IMediaFileStoreCache _mediaFileStoreCache; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; + _authorizationService = authorizationService; + // Resolve from service provider as the service will not be registered if configuration is invalid. + _mediaFileStoreCache = serviceProvider.GetService(); + _notifier = notifier; + H = htmlLocalizer; + } - public MediaCacheController( - IAuthorizationService authorizationService, - IServiceProvider serviceProvider, - INotifier notifier, - IHtmlLocalizer htmlLocalizer - ) + [Admin("MediaCache", "MediaCache.Index")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, MediaCachePermissions.ManageAssetCache)) { - _authorizationService = authorizationService; - // Resolve from service provider as the service will not be registered if configuration is invalid. - _mediaFileStoreCache = serviceProvider.GetService(); - _notifier = notifier; - H = htmlLocalizer; + return Forbid(); } - - [Admin("MediaCache", "MediaCache.Index")] - public async Task Index() + var model = new MediaCacheViewModel { - if (!await _authorizationService.AuthorizeAsync(User, MediaCachePermissions.ManageAssetCache)) - { - return Forbid(); - } - var model = new MediaCacheViewModel - { - IsConfigured = _mediaFileStoreCache != null - }; + IsConfigured = _mediaFileStoreCache != null + }; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Purge() + [HttpPost] + public async Task Purge() + { + if (!await _authorizationService.AuthorizeAsync(User, MediaCachePermissions.ManageAssetCache)) { - if (!await _authorizationService.AuthorizeAsync(User, MediaCachePermissions.ManageAssetCache)) - { - return Forbid(); - } - - if (_mediaFileStoreCache == null) - { - await _notifier.ErrorAsync(H["The asset cache feature is enabled, but a remote media store feature is not enabled, or not configured with appsettings.json."]); - RedirectToAction(nameof(Index)); - } + return Forbid(); + } - var hasErrors = await _mediaFileStoreCache.PurgeAsync(); - if (hasErrors) - { - await _notifier.ErrorAsync(H["Asset cache purged, with errors."]); - } - else - { - await _notifier.InformationAsync(H["Asset cache purged."]); - } + if (_mediaFileStoreCache == null) + { + await _notifier.ErrorAsync(H["The asset cache feature is enabled, but a remote media store feature is not enabled, or not configured with appsettings.json."]); + RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + var hasErrors = await _mediaFileStoreCache.PurgeAsync(); + if (hasErrors) + { + await _notifier.ErrorAsync(H["Asset cache purged, with errors."]); + } + else + { + await _notifier.InformationAsync(H["Asset cache purged."]); } + + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaProfilesController.cs b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaProfilesController.cs index 172502aad50..2efb83ae180 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaProfilesController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Controllers/MediaProfilesController.cs @@ -19,351 +19,350 @@ using OrchardCore.Navigation; using OrchardCore.Routing; -namespace OrchardCore.Media.Controllers +namespace OrchardCore.Media.Controllers; + +[Admin("MediaProfiles/{action}", "MediaProfiles.{action}")] +public class MediaProfilesController : Controller { - [Admin("MediaProfiles/{action}", "MediaProfiles.{action}")] - public class MediaProfilesController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly MediaProfilesManager _mediaProfilesManager; + private readonly MediaOptions _mediaOptions; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public MediaProfilesController( + IAuthorizationService authorizationService, + MediaProfilesManager mediaProfilesManager, + IOptions mediaOptions, + IOptions pagerOptions, + INotifier notifier, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer + ) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly MediaProfilesManager _mediaProfilesManager; - private readonly MediaOptions _mediaOptions; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public MediaProfilesController( - IAuthorizationService authorizationService, - MediaProfilesManager mediaProfilesManager, - IOptions mediaOptions, - IOptions pagerOptions, - INotifier notifier, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer - ) - { - _authorizationService = authorizationService; - _mediaProfilesManager = mediaProfilesManager; - _mediaOptions = mediaOptions.Value; - _pagerOptions = pagerOptions.Value; - _notifier = notifier; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - } + _authorizationService = authorizationService; + _mediaProfilesManager = mediaProfilesManager; + _mediaOptions = mediaOptions.Value; + _pagerOptions = pagerOptions.Value; + _notifier = notifier; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + } - [Admin("MediaProfiles", "MediaProfiles.Index")] - public async Task Index(ContentOptions options, PagerParameters pagerParameters) + [Admin("MediaProfiles", "MediaProfiles.Index")] + public async Task Index(ContentOptions options, PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) - { - return Forbid(); - } + return Forbid(); + } - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - var mediaProfiles = mediaProfilesDocument.MediaProfiles.ToList(); + var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); + var mediaProfiles = mediaProfilesDocument.MediaProfiles.ToList(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - mediaProfiles = mediaProfiles.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + if (!string.IsNullOrWhiteSpace(options.Search)) + { + mediaProfiles = mediaProfiles.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - var count = mediaProfiles.Count; + var count = mediaProfiles.Count; - mediaProfiles = mediaProfiles.OrderBy(x => x.Key) - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); + mediaProfiles = mediaProfiles.OrderBy(x => x.Key) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ToList(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var model = new MediaProfileIndexViewModel - { - MediaProfiles = mediaProfiles.Select(x => new MediaProfileEntry { Name = x.Key, MediaProfile = x.Value }).ToList(), - Pager = pagerShape - }; + var model = new MediaProfileIndexViewModel + { + MediaProfiles = mediaProfiles.Select(x => new MediaProfileEntry { Name = x.Key, MediaProfile = x.Value }).ToList(), + Pager = pagerShape + }; - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - return View(model); - } + return View(model); + } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(MediaProfileIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(MediaProfileIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - public async Task Create() + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) - { - return Forbid(); - } + return Forbid(); + } - var model = new MediaProfileViewModel(); + var model = new MediaProfileViewModel(); - BuildViewModel(model); + BuildViewModel(model); - return View(model); - } + return View(model); + } - [HttpPost, ActionName(nameof(Create))] - public async Task CreatePost(MediaProfileViewModel model, string submit) + [HttpPost, ActionName(nameof(Create))] + public async Task CreatePost(MediaProfileViewModel model, string submit) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) - { - return Forbid(); - } + return Forbid(); + } - if (ModelState.IsValid) + if (ModelState.IsValid) + { + if (string.IsNullOrWhiteSpace(model.Name)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["The name is mandatory."]); - } - else - { - var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - - if (mediaProfilesDocument.MediaProfiles.ContainsKey(model.Name)) - { - ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["A profile with the same name already exists."]); - } - } + ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["The name is mandatory."]); } - - if (ModelState.IsValid) + else { - var isCustomWidth = model.SelectedWidth != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedWidth) < 0; - var isCustomHeight = model.SelectedHeight != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedHeight) < 0; + var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - var mediaProfile = new MediaProfile - { - Hint = model.Hint, - Width = isCustomWidth ? model.CustomWidth : model.SelectedWidth, - Height = isCustomHeight ? model.CustomHeight : model.SelectedHeight, - Mode = model.SelectedMode, - Format = model.SelectedFormat, - Quality = model.Quality, - BackgroundColor = model.BackgroundColor - }; - - await _mediaProfilesManager.UpdateMediaProfileAsync(model.Name, mediaProfile); - - if (submit == "SaveAndContinue") - { - return RedirectToAction(nameof(Edit), new { name = model.Name }); - } - else + if (mediaProfilesDocument.MediaProfiles.ContainsKey(model.Name)) { - return RedirectToAction(nameof(Index)); + ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["A profile with the same name already exists."]); } } - - // If we got this far, something failed, redisplay form - BuildViewModel(model); - - return View(model); } - public async Task Edit(string name) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) - { - return Forbid(); - } - - var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - - if (!mediaProfilesDocument.MediaProfiles.TryGetValue(name, out var mediaProfile)) - { - return RedirectToAction(nameof(Create), new { name }); - } - - // Is a custom width if the width is not 0 and it is not in the array of supported sizes. - var isCustomWidth = mediaProfile.Width != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, mediaProfile.Width) < 0; - var isCustomHeight = mediaProfile.Height != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, mediaProfile.Height) < 0; + var isCustomWidth = model.SelectedWidth != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedWidth) < 0; + var isCustomHeight = model.SelectedHeight != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedHeight) < 0; - var model = new MediaProfileViewModel + var mediaProfile = new MediaProfile { - Name = name, - Hint = mediaProfile.Hint, - SelectedWidth = isCustomWidth ? -1 : mediaProfile.Width, - CustomWidth = isCustomWidth ? mediaProfile.Width : 0, - SelectedHeight = isCustomHeight ? -1 : mediaProfile.Height, - CustomHeight = isCustomHeight ? mediaProfile.Height : 0, - SelectedMode = mediaProfile.Mode, - SelectedFormat = mediaProfile.Format, - Quality = mediaProfile.Quality, - BackgroundColor = mediaProfile.BackgroundColor + Hint = model.Hint, + Width = isCustomWidth ? model.CustomWidth : model.SelectedWidth, + Height = isCustomHeight ? model.CustomHeight : model.SelectedHeight, + Mode = model.SelectedMode, + Format = model.SelectedFormat, + Quality = model.Quality, + BackgroundColor = model.BackgroundColor }; - BuildViewModel(model); - - return View(model); - } + await _mediaProfilesManager.UpdateMediaProfileAsync(model.Name, mediaProfile); - [HttpPost] - public async Task Edit(string sourceName, MediaProfileViewModel model, string submit) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + if (submit == "SaveAndContinue") { - return Forbid(); + return RedirectToAction(nameof(Edit), new { name = model.Name }); } - - var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); - - if (!mediaProfilesDocument.MediaProfiles.ContainsKey(sourceName)) + else { - return NotFound(); + return RedirectToAction(nameof(Index)); } + } - if (ModelState.IsValid) - { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["The name is mandatory."]); - } - } + // If we got this far, something failed, redisplay form + BuildViewModel(model); - if (ModelState.IsValid) - { - var isCustomWidth = Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedWidth) < 0; - var isCustomHeight = Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedHeight) < 0; + return View(model); + } - var mediaProfile = new MediaProfile - { - Hint = model.Hint, - Width = isCustomWidth ? model.CustomWidth : model.SelectedWidth, - Height = isCustomHeight ? model.CustomHeight : model.SelectedHeight, - Mode = model.SelectedMode, - Format = model.SelectedFormat, - Quality = model.Quality, - BackgroundColor = model.BackgroundColor - }; + public async Task Edit(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + { + return Forbid(); + } - await _mediaProfilesManager.RemoveMediaProfileAsync(sourceName); + var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - await _mediaProfilesManager.UpdateMediaProfileAsync(model.Name, mediaProfile); + if (!mediaProfilesDocument.MediaProfiles.TryGetValue(name, out var mediaProfile)) + { + return RedirectToAction(nameof(Create), new { name }); + } - if (submit != "SaveAndContinue") - { - return RedirectToAction(nameof(Index)); - } - } + // Is a custom width if the width is not 0 and it is not in the array of supported sizes. + var isCustomWidth = mediaProfile.Width != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, mediaProfile.Width) < 0; + var isCustomHeight = mediaProfile.Height != 0 && Array.BinarySearch(_mediaOptions.SupportedSizes, mediaProfile.Height) < 0; + + var model = new MediaProfileViewModel + { + Name = name, + Hint = mediaProfile.Hint, + SelectedWidth = isCustomWidth ? -1 : mediaProfile.Width, + CustomWidth = isCustomWidth ? mediaProfile.Width : 0, + SelectedHeight = isCustomHeight ? -1 : mediaProfile.Height, + CustomHeight = isCustomHeight ? mediaProfile.Height : 0, + SelectedMode = mediaProfile.Mode, + SelectedFormat = mediaProfile.Format, + Quality = mediaProfile.Quality, + BackgroundColor = mediaProfile.BackgroundColor + }; + + BuildViewModel(model); + + return View(model); + } - // If we got this far, something failed, redisplay form - BuildViewModel(model); + [HttpPost] + public async Task Edit(string sourceName, MediaProfileViewModel model, string submit) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + { + return Forbid(); + } - // If the name was changed or removed, prevent a 404 or a failure on the next post. - model.Name = sourceName; + var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); - return View(model); + if (!mediaProfilesDocument.MediaProfiles.ContainsKey(sourceName)) + { + return NotFound(); } - [HttpPost] - public async Task Delete(string name) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(MediaProfileViewModel.Name), S["The name is mandatory."]); } + } - var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); + if (ModelState.IsValid) + { + var isCustomWidth = Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedWidth) < 0; + var isCustomHeight = Array.BinarySearch(_mediaOptions.SupportedSizes, model.SelectedHeight) < 0; - if (!mediaProfilesDocument.MediaProfiles.ContainsKey(name)) + var mediaProfile = new MediaProfile { - return NotFound(); + Hint = model.Hint, + Width = isCustomWidth ? model.CustomWidth : model.SelectedWidth, + Height = isCustomHeight ? model.CustomHeight : model.SelectedHeight, + Mode = model.SelectedMode, + Format = model.SelectedFormat, + Quality = model.Quality, + BackgroundColor = model.BackgroundColor + }; + + await _mediaProfilesManager.RemoveMediaProfileAsync(sourceName); + + await _mediaProfilesManager.UpdateMediaProfileAsync(model.Name, mediaProfile); + + if (submit != "SaveAndContinue") + { + return RedirectToAction(nameof(Index)); } + } + + // If we got this far, something failed, redisplay form + BuildViewModel(model); - await _mediaProfilesManager.RemoveMediaProfileAsync(name); + // If the name was changed or removed, prevent a 404 or a failure on the next post. + model.Name = sourceName; - await _notifier.SuccessAsync(H["Media profile deleted successfully."]); + return View(model); + } - return RedirectToAction(nameof(Index)); + [HttpPost] + public async Task Delete(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + { + return Forbid(); } - [HttpPost, ActionName("Index")] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); + + if (!mediaProfilesDocument.MediaProfiles.ContainsKey(name)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) - { - return Forbid(); - } + return NotFound(); + } - if (itemIds?.Count() > 0) - { - var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); - var checkedContentItems = mediaProfilesDocument.MediaProfiles.Where(x => itemIds.Contains(x.Key)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _mediaProfilesManager.RemoveMediaProfileAsync(item.Key); - } - await _notifier.SuccessAsync(H["Media profiles successfully removed."]); - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } - } + await _mediaProfilesManager.RemoveMediaProfileAsync(name); - return RedirectToAction(nameof(Index)); + await _notifier.SuccessAsync(H["Media profile deleted successfully."]); + + return RedirectToAction(nameof(Index)); + } + + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMediaProfiles)) + { + return Forbid(); } - private void BuildViewModel(MediaProfileViewModel model) + if (itemIds?.Count() > 0) { - model.AvailableWidths.Add(new SelectListItem() { Text = S["Default"], Value = "0" }); - model.AvailableHeights.Add(new SelectListItem() { Text = S["Default"], Value = "0" }); - model.AvailableWidths.AddRange(_mediaOptions.SupportedSizes.Select(x => new SelectListItem() { Text = x.ToString(), Value = x.ToString() })); - model.AvailableHeights.AddRange(_mediaOptions.SupportedSizes.Select(x => new SelectListItem() { Text = x.ToString(), Value = x.ToString() })); - if (_mediaOptions.UseTokenizedQueryString) + var mediaProfilesDocument = await _mediaProfilesManager.LoadMediaProfilesDocumentAsync(); + var checkedContentItems = mediaProfilesDocument.MediaProfiles.Where(x => itemIds.Contains(x.Key)); + switch (options.BulkAction) { - model.AvailableWidths.Add(new SelectListItem() { Text = S["Custom Size"], Value = "-1" }); - model.AvailableHeights.Add(new SelectListItem() { Text = S["Custom Size"], Value = "-1" }); + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _mediaProfilesManager.RemoveMediaProfileAsync(item.Key); + } + await _notifier.SuccessAsync(H["Media profiles successfully removed."]); + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } + } + + return RedirectToAction(nameof(Index)); + } - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Default (Max)"], Value = ((int)ResizeMode.Undefined).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Max"], Value = ((int)ResizeMode.Max).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Crop"], Value = ((int)ResizeMode.Crop).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Pad"], Value = ((int)ResizeMode.Pad).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["BoxPad"], Value = ((int)ResizeMode.BoxPad).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Min"], Value = ((int)ResizeMode.Min).ToString() }); - model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Stretch"], Value = ((int)ResizeMode.Stretch).ToString() }); - - - model.AvailableFormats.Add(new SelectListItem() { Text = S["Default"], Value = ((int)Format.Undefined).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["Bmp"], Value = ((int)Format.Bmp).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["Gif"], Value = ((int)Format.Gif).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["Jpg"], Value = ((int)Format.Jpg).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["Png"], Value = ((int)Format.Png).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["Tga"], Value = ((int)Format.Tga).ToString() }); - model.AvailableFormats.Add(new SelectListItem() { Text = S["WebP"], Value = ((int)Format.WebP).ToString() }); + private void BuildViewModel(MediaProfileViewModel model) + { + model.AvailableWidths.Add(new SelectListItem() { Text = S["Default"], Value = "0" }); + model.AvailableHeights.Add(new SelectListItem() { Text = S["Default"], Value = "0" }); + model.AvailableWidths.AddRange(_mediaOptions.SupportedSizes.Select(x => new SelectListItem() { Text = x.ToString(), Value = x.ToString() })); + model.AvailableHeights.AddRange(_mediaOptions.SupportedSizes.Select(x => new SelectListItem() { Text = x.ToString(), Value = x.ToString() })); + if (_mediaOptions.UseTokenizedQueryString) + { + model.AvailableWidths.Add(new SelectListItem() { Text = S["Custom Size"], Value = "-1" }); + model.AvailableHeights.Add(new SelectListItem() { Text = S["Custom Size"], Value = "-1" }); } + + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Default (Max)"], Value = ((int)ResizeMode.Undefined).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Max"], Value = ((int)ResizeMode.Max).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Crop"], Value = ((int)ResizeMode.Crop).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Pad"], Value = ((int)ResizeMode.Pad).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["BoxPad"], Value = ((int)ResizeMode.BoxPad).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Min"], Value = ((int)ResizeMode.Min).ToString() }); + model.AvailableResizeModes.Add(new SelectListItem() { Text = S["Stretch"], Value = ((int)ResizeMode.Stretch).ToString() }); + + + model.AvailableFormats.Add(new SelectListItem() { Text = S["Default"], Value = ((int)Format.Undefined).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["Bmp"], Value = ((int)Format.Bmp).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["Gif"], Value = ((int)Format.Gif).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["Jpg"], Value = ((int)Format.Jpg).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["Png"], Value = ((int)Format.Png).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["Tga"], Value = ((int)Format.Tga).ToString() }); + model.AvailableFormats.Add(new SelectListItem() { Text = S["WebP"], Value = ((int)Format.WebP).ToString() }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentSource.cs index 0c64a52b54b..53ecf8ab3fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentSource.cs @@ -3,31 +3,30 @@ using OrchardCore.Deployment; using OrchardCore.Media.Services; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +public class AllMediaProfilesDeploymentSource : IDeploymentSource { - public class AllMediaProfilesDeploymentSource : IDeploymentSource + private readonly MediaProfilesManager _mediaProfilesManager; + + public AllMediaProfilesDeploymentSource(MediaProfilesManager mediaProfilesManager) { - private readonly MediaProfilesManager _mediaProfilesManager; + _mediaProfilesManager = mediaProfilesManager; + } - public AllMediaProfilesDeploymentSource(MediaProfilesManager mediaProfilesManager) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AllMediaProfilesDeploymentStep) { - _mediaProfilesManager = mediaProfilesManager; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not AllMediaProfilesDeploymentStep) - { - return; - } - - var mediaProfiles = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); + var mediaProfiles = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - result.Steps.Add(new JsonObject - { - ["name"] = "MediaProfiles", - ["MediaProfiles"] = JObject.FromObject(mediaProfiles.MediaProfiles), - }); - } + result.Steps.Add(new JsonObject + { + ["name"] = "MediaProfiles", + ["MediaProfiles"] = JObject.FromObject(mediaProfiles.MediaProfiles), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStep.cs index f13e6ceba1e..eb5aae8d5d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +/// +/// Adds media profiles to a . +/// +public class AllMediaProfilesDeploymentStep : DeploymentStep { - /// - /// Adds media profiles to a . - /// - public class AllMediaProfilesDeploymentStep : DeploymentStep + public AllMediaProfilesDeploymentStep() { - public AllMediaProfilesDeploymentStep() - { - Name = "AllMediaProfiles"; - } + Name = "AllMediaProfiles"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStepDriver.cs index 02978d819cd..6e6765f6a4c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/AllMediaProfilesDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +public sealed class AllMediaProfilesDeploymentStepDriver : DisplayDriver { - public sealed class AllMediaProfilesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllMediaProfilesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllMediaProfilesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllMediaProfilesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllMediaProfilesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllMediaProfilesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllMediaProfilesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllMediaProfilesDeploymentStep step, BuildEditorContext context) - { - return View("AllMediaProfilesDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllMediaProfilesDeploymentStep step, BuildEditorContext context) + { + return View("AllMediaProfilesDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentSource.cs index ac818c320dc..d00f219832e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentSource.cs @@ -4,63 +4,62 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +public class MediaDeploymentSource : IDeploymentSource { - public class MediaDeploymentSource : IDeploymentSource + private readonly IMediaFileStore _mediaFileStore; + + public MediaDeploymentSource(IMediaFileStore mediaFileStore) { - private readonly IMediaFileStore _mediaFileStore; + _mediaFileStore = mediaFileStore; + } - public MediaDeploymentSource(IMediaFileStore mediaFileStore) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not MediaDeploymentStep mediaStep) { - _mediaFileStore = mediaFileStore; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not MediaDeploymentStep mediaStep) - { - return; - } + IAsyncEnumerable paths = null; - IAsyncEnumerable paths = null; + if (mediaStep.IncludeAll) + { + paths = _mediaFileStore.GetDirectoryContentAsync(null, true).Where(e => !e.IsDirectory).Select(e => e.Path); + } + else + { + paths = new List(mediaStep.FilePaths ?? []).ToAsyncEnumerable(); - if (mediaStep.IncludeAll) + foreach (var directoryPath in mediaStep.DirectoryPaths ?? []) { - paths = _mediaFileStore.GetDirectoryContentAsync(null, true).Where(e => !e.IsDirectory).Select(e => e.Path); + paths = paths.Concat(_mediaFileStore.GetDirectoryContentAsync(directoryPath, true).Where(e => !e.IsDirectory).Select(e => e.Path)); } - else - { - paths = new List(mediaStep.FilePaths ?? []).ToAsyncEnumerable(); - - foreach (var directoryPath in mediaStep.DirectoryPaths ?? []) - { - paths = paths.Concat(_mediaFileStore.GetDirectoryContentAsync(directoryPath, true).Where(e => !e.IsDirectory).Select(e => e.Path)); - } - paths = paths.OrderBy(p => p); - } - - var output = await paths.Select(p => new MediaDeploymentStepModel { SourcePath = p, TargetPath = p }).ToArrayAsync(); + paths = paths.OrderBy(p => p); + } - foreach (var path in output) - { - var stream = await _mediaFileStore.GetFileStreamAsync(path.SourcePath); + var output = await paths.Select(p => new MediaDeploymentStepModel { SourcePath = p, TargetPath = p }).ToArrayAsync(); - await result.FileBuilder.SetFileAsync(path.SourcePath, stream); - } + foreach (var path in output) + { + var stream = await _mediaFileStore.GetFileStreamAsync(path.SourcePath); - // Adding media files - result.Steps.Add(new JsonObject - { - ["name"] = "media", - ["Files"] = JArray.FromObject(output), - }); + await result.FileBuilder.SetFileAsync(path.SourcePath, stream); } - private sealed class MediaDeploymentStepModel + // Adding media files + result.Steps.Add(new JsonObject { - public string SourcePath { get; set; } - public string TargetPath { get; set; } - } + ["name"] = "media", + ["Files"] = JArray.FromObject(output), + }); + } + + private sealed class MediaDeploymentStepModel + { + public string SourcePath { get; set; } + public string TargetPath { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStep.cs index 89663aa0aeb..c0189b58459 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStep.cs @@ -1,21 +1,20 @@ using OrchardCore.Deployment; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +/// +/// Adds layers to a . +/// +public class MediaDeploymentStep : DeploymentStep { - /// - /// Adds layers to a . - /// - public class MediaDeploymentStep : DeploymentStep + public MediaDeploymentStep() { - public MediaDeploymentStep() - { - Name = "Media"; - } + Name = "Media"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] FilePaths { get; set; } + public string[] FilePaths { get; set; } - public string[] DirectoryPaths { get; set; } - } + public string[] DirectoryPaths { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStepDriver.cs index 84daf05c094..9aae07c7788 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Deployment/MediaDeploymentStepDriver.cs @@ -6,78 +6,77 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Media.ViewModels; -namespace OrchardCore.Media.Deployment +namespace OrchardCore.Media.Deployment; + +public sealed class MediaDeploymentStepDriver : DisplayDriver { - public sealed class MediaDeploymentStepDriver : DisplayDriver + private readonly IMediaFileStore _mediaFileStore; + + public MediaDeploymentStepDriver(IMediaFileStore mediaFileStore) { - private readonly IMediaFileStore _mediaFileStore; + _mediaFileStore = mediaFileStore; + } - public MediaDeploymentStepDriver(IMediaFileStore mediaFileStore) - { - _mediaFileStore = mediaFileStore; - } + public override Task DisplayAsync(MediaDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("MediaDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("MediaDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(MediaDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(MediaDeploymentStep step, BuildEditorContext context) + { + return Initialize("MediaDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("MediaDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("MediaDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.FilePaths = step.FilePaths; + model.DirectoryPaths = step.DirectoryPaths; + model.Entries = await GetMediaStoreEntries(); + }).Location("Content"); + } - public override IDisplayResult Edit(MediaDeploymentStep step, BuildEditorContext context) - { - return Initialize("MediaDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.FilePaths = step.FilePaths; - model.DirectoryPaths = step.DirectoryPaths; - model.Entries = await GetMediaStoreEntries(); - }).Location("Content"); - } + public override async Task UpdateAsync(MediaDeploymentStep step, UpdateEditorContext context) + { + step.FilePaths = []; + step.DirectoryPaths = []; + + await context.Updater.TryUpdateModelAsync(step, + Prefix, + x => x.FilePaths, + x => x.DirectoryPaths, + x => x.IncludeAll); - public override async Task UpdateAsync(MediaDeploymentStep step, UpdateEditorContext context) + // Don't have the selected option if include all. + if (step.IncludeAll) { step.FilePaths = []; step.DirectoryPaths = []; + } - await context.Updater.TryUpdateModelAsync(step, - Prefix, - x => x.FilePaths, - x => x.DirectoryPaths, - x => x.IncludeAll); + return Edit(step, context); + } - // Don't have the selected option if include all. - if (step.IncludeAll) + private async Task> GetMediaStoreEntries(string path = null, MediaStoreEntryViewModel parent = null) + { + var mediaStoreEntries = await _mediaFileStore.GetDirectoryContentAsync(path) + .SelectAwait(async e => { - step.FilePaths = []; - step.DirectoryPaths = []; - } - - return Edit(step, context); - } - - private async Task> GetMediaStoreEntries(string path = null, MediaStoreEntryViewModel parent = null) - { - var mediaStoreEntries = await _mediaFileStore.GetDirectoryContentAsync(path) - .SelectAwait(async e => + var mediaStoreEntry = new MediaStoreEntryViewModel { - var mediaStoreEntry = new MediaStoreEntryViewModel - { - Name = e.Name, - Path = e.Path, - Parent = parent - }; + Name = e.Name, + Path = e.Path, + Parent = parent + }; - mediaStoreEntry.Entries = e.IsDirectory - ? await GetMediaStoreEntries(e.Path, mediaStoreEntry) - : []; + mediaStoreEntry.Entries = e.IsDirectory + ? await GetMediaStoreEntries(e.Path, mediaStoreEntry) + : []; - return mediaStoreEntry; - }).ToListAsync(); + return mediaStoreEntry; + }).ToListAsync(); - return mediaStoreEntries; - } + return mediaStoreEntries; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Drivers/MediaFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Media/Drivers/MediaFieldDisplayDriver.cs index 1fbd0d1c6e3..26339d51a60 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Drivers/MediaFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Drivers/MediaFieldDisplayDriver.cs @@ -17,149 +17,148 @@ using OrchardCore.Media.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Media.Drivers +namespace OrchardCore.Media.Drivers; + +public sealed class MediaFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class MediaFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - private readonly ILogger _logger; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + private readonly ILogger _logger; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public MediaFieldDisplayDriver(AttachedMediaFieldFileService attachedMediaFieldFileService, - IStringLocalizer localizer, - ILogger logger) - { - _attachedMediaFieldFileService = attachedMediaFieldFileService; - S = localizer; - _logger = logger; - } + public MediaFieldDisplayDriver(AttachedMediaFieldFileService attachedMediaFieldFileService, + IStringLocalizer localizer, + ILogger logger) + { + _attachedMediaFieldFileService = attachedMediaFieldFileService; + S = localizer; + _logger = logger; + } - public override IDisplayResult Display(MediaField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(MediaField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }) - .Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }) + .Location("Detail", "Content") + .Location("Summary", "Content"); + } + + public override IDisplayResult Edit(MediaField field, BuildFieldEditorContext context) + { + var itemPaths = field.Paths?.ToList().Select(p => new EditMediaFieldItemInfo { Path = p }).ToArray() ?? []; - public override IDisplayResult Edit(MediaField field, BuildFieldEditorContext context) + return Initialize(GetEditorShapeType(context), model => { - var itemPaths = field.Paths?.ToList().Select(p => new EditMediaFieldItemInfo { Path = p }).ToArray() ?? []; + var settings = context.PartFieldDefinition.GetSettings(); - return Initialize(GetEditorShapeType(context), model => + for (var i = 0; i < itemPaths.Length; i++) { - var settings = context.PartFieldDefinition.GetSettings(); - - for (var i = 0; i < itemPaths.Length; i++) + if (settings.AllowMediaText && i < field.MediaTexts?.Length) { - if (settings.AllowMediaText && i < field.MediaTexts?.Length) - { - itemPaths[i].MediaText = field.MediaTexts[i]; - } + itemPaths[i].MediaText = field.MediaTexts[i]; + } - if (settings.AllowAnchors) + if (settings.AllowAnchors) + { + var anchors = field.GetAnchors(); + if (anchors != null && i < anchors.Length) { - var anchors = field.GetAnchors(); - if (anchors != null && i < anchors.Length) - { - itemPaths[i].Anchor = anchors[i]; - } + itemPaths[i].Anchor = anchors[i]; } + } - var filenames = field.GetAttachedFileNames(); - if (filenames != null && i < filenames.Length) - { - itemPaths[i].AttachedFileName = filenames[i]; - } + var filenames = field.GetAttachedFileNames(); + if (filenames != null && i < filenames.Length) + { + itemPaths[i].AttachedFileName = filenames[i]; } + } - model.Paths = JConvert.SerializeObject(itemPaths, JOptions.CamelCase); - model.TempUploadFolder = _attachedMediaFieldFileService.GetMediaFieldsTempSubFolder(); - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - model.AllowMediaText = settings.AllowMediaText; - model.AllowedExtensions = settings.AllowedExtensions ?? []; - }); - } + model.Paths = JConvert.SerializeObject(itemPaths, JOptions.CamelCase); + model.TempUploadFolder = _attachedMediaFieldFileService.GetMediaFieldsTempSubFolder(); + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + model.AllowMediaText = settings.AllowMediaText; + model.AllowedExtensions = settings.AllowedExtensions ?? []; + }); + } - public override async Task UpdateAsync(MediaField field, UpdateFieldEditorContext context) - { - var model = new EditMediaFieldViewModel(); + public override async Task UpdateAsync(MediaField field, UpdateFieldEditorContext context) + { + var model = new EditMediaFieldViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.Paths); + await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.Paths); - // Deserializing an empty string doesn't return an array - var items = string.IsNullOrWhiteSpace(model.Paths) - ? [] - : JConvert.DeserializeObject>(model.Paths, JOptions.CamelCase); + // Deserializing an empty string doesn't return an array + var items = string.IsNullOrWhiteSpace(model.Paths) + ? [] + : JConvert.DeserializeObject>(model.Paths, JOptions.CamelCase); - // If it's an attached media field editor the files are automatically handled by _attachedMediaFieldFileService. - if (string.Equals(context.PartFieldDefinition.Editor(), "Attached", StringComparison.OrdinalIgnoreCase)) + // If it's an attached media field editor the files are automatically handled by _attachedMediaFieldFileService. + if (string.Equals(context.PartFieldDefinition.Editor(), "Attached", StringComparison.OrdinalIgnoreCase)) + { + try { - try - { - field.SetAttachedFileNames(items.Where(i => !i.IsRemoved).Select(i => i.AttachedFileName).ToArray()); - await _attachedMediaFieldFileService.HandleFilesOnFieldUpdateAsync(items, context.ContentPart.ContentItem); - } - catch (Exception e) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["{0}: There was an error handling the files.", context.PartFieldDefinition.DisplayName()]); - _logger.LogError(e, "Error handling attached media files for field '{Field}'", context.PartFieldDefinition.DisplayName()); - } + field.SetAttachedFileNames(items.Where(i => !i.IsRemoved).Select(i => i.AttachedFileName).ToArray()); + await _attachedMediaFieldFileService.HandleFilesOnFieldUpdateAsync(items, context.ContentPart.ContentItem); + } + catch (Exception e) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["{0}: There was an error handling the files.", context.PartFieldDefinition.DisplayName()]); + _logger.LogError(e, "Error handling attached media files for field '{Field}'", context.PartFieldDefinition.DisplayName()); } + } - field.Paths = items.Where(p => !p.IsRemoved).Select(p => p.Path).ToArray(); + field.Paths = items.Where(p => !p.IsRemoved).Select(p => p.Path).ToArray(); - var settings = context.PartFieldDefinition.GetSettings(); + var settings = context.PartFieldDefinition.GetSettings(); - if (settings.AllowedExtensions?.Length > 0) + if (settings.AllowedExtensions?.Length > 0) + { + for (var i = 0; i < field.Paths.Length; i++) { - for (var i = 0; i < field.Paths.Length; i++) - { - var extension = Path.GetExtension(field.Paths[i]); + var extension = Path.GetExtension(field.Paths[i]); - if (!settings.AllowedExtensions.Contains(extension)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["Media extension is not allowed. Only media with '{0}' extensions are allowed.", string.Join(", ", settings.AllowedExtensions)]); - } + if (!settings.AllowedExtensions.Contains(extension)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["Media extension is not allowed. Only media with '{0}' extensions are allowed.", string.Join(", ", settings.AllowedExtensions)]); } } + } - if (settings.Required && field.Paths.Length < 1) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } - - if (field.Paths.Length > 1 && !settings.Multiple) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["{0}: Selecting multiple media is forbidden.", context.PartFieldDefinition.DisplayName()]); - } + if (settings.Required && field.Paths.Length < 1) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } - if (settings.AllowMediaText) - { - field.MediaTexts = items.Select(t => t.MediaText).ToArray(); - } - else - { - field.MediaTexts = []; - } + if (field.Paths.Length > 1 && !settings.Multiple) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Paths), S["{0}: Selecting multiple media is forbidden.", context.PartFieldDefinition.DisplayName()]); + } - if (settings.AllowAnchors) - { - field.SetAnchors(items.Select(t => t.Anchor).ToArray()); - } - else if (field.Content.ContainsKey("Anchors")) // Less well known properties should be self healing. - { - field.Content.Remove("Anchors"); - } + if (settings.AllowMediaText) + { + field.MediaTexts = items.Select(t => t.MediaText).ToArray(); + } + else + { + field.MediaTexts = []; + } - return Edit(field, context); + if (settings.AllowAnchors) + { + field.SetAnchors(items.Select(t => t.Anchor).ToArray()); + } + else if (field.Content.ContainsKey("Anchors")) // Less well known properties should be self healing. + { + field.Content.Remove("Anchors"); } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Fields/Anchor.cs b/src/OrchardCore.Modules/OrchardCore.Media/Fields/Anchor.cs index 7e9e067f2c2..3abf7be90b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Fields/Anchor.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Fields/Anchor.cs @@ -1,12 +1,11 @@ -namespace OrchardCore.Media.Fields +namespace OrchardCore.Media.Fields; + +/// +/// An anchor represents two floats with an and position. +/// When anchoring is enabled the position defaults to the center of the media. +/// +public class Anchor { - /// - /// An anchor represents two floats with an and position. - /// When anchoring is enabled the position defaults to the center of the media. - /// - public class Anchor - { - public float X { get; set; } = 0.5f; - public float Y { get; set; } = 0.5f; - } + public float X { get; set; } = 0.5f; + public float Y { get; set; } = 0.5f; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAnchorExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAnchorExtensions.cs index 2a35ce75c30..48168b29b56 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAnchorExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAnchorExtensions.cs @@ -1,26 +1,25 @@ using System.Text.Json.Nodes; -namespace OrchardCore.Media.Fields +namespace OrchardCore.Media.Fields; + +public static class MediaFieldAnchorExtensions { - public static class MediaFieldAnchorExtensions + /// + /// Anchors are a less well known property of a media field. + /// + public static Anchor[] GetAnchors(this MediaField mediaField) { - /// - /// Anchors are a less well known property of a media field. - /// - public static Anchor[] GetAnchors(this MediaField mediaField) - { - var anchors = (JsonArray)mediaField.Content["Anchors"]; - - return anchors is not null ? anchors.ToObject() : []; - } + var anchors = (JsonArray)mediaField.Content["Anchors"]; - /// - /// Tags names are a less well known property of a media field. - /// - public static void SetAnchors(this MediaField mediaField, Anchor[] anchors) - { - mediaField.Content["Anchors"] = JArray.FromObject(anchors); - } + return anchors is not null ? anchors.ToObject() : []; + } + /// + /// Tags names are a less well known property of a media field. + /// + public static void SetAnchors(this MediaField mediaField, Anchor[] anchors) + { + mediaField.Content["Anchors"] = JArray.FromObject(anchors); } + } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAttachedFileNameExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAttachedFileNameExtensions.cs index 623368b566e..45a845d663e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAttachedFileNameExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Fields/MediaFieldAttachedFileNameExtensions.cs @@ -1,28 +1,27 @@ using System.Text.Json.Nodes; -namespace OrchardCore.Media.Fields +namespace OrchardCore.Media.Fields; + +public static class MediaFieldAttachedFileNameExtensions { - public static class MediaFieldAttachedFileNameExtensions + /// + /// Gets the names of attached files. + /// + public static string[] GetAttachedFileNames(this MediaField mediaField) { - /// - /// Gets the names of attached files. - /// - public static string[] GetAttachedFileNames(this MediaField mediaField) - { - var filenames = (JsonArray)mediaField.Content["AttachedFileNames"]; - - return filenames != null - ? filenames.ToObject() - : []; - } + var filenames = (JsonArray)mediaField.Content["AttachedFileNames"]; - /// - /// Sets the names of attached files. - /// - public static void SetAttachedFileNames(this MediaField mediaField, string[] filenames) - { - mediaField.Content["AttachedFileNames"] = JArray.FromObject(filenames); - } + return filenames != null + ? filenames.ToObject() + : []; + } + /// + /// Sets the names of attached files. + /// + public static void SetAttachedFileNames(this MediaField mediaField, string[] filenames) + { + mediaField.Content["AttachedFileNames"] = JArray.FromObject(filenames); } + } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Filters/AssetUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.Media/Filters/AssetUrlFilter.cs index 44250ba39e3..888da005bcd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Filters/AssetUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Filters/AssetUrlFilter.cs @@ -3,22 +3,21 @@ using Fluid.Values; using OrchardCore.Liquid; -namespace OrchardCore.Media.Filters +namespace OrchardCore.Media.Filters; + +public class AssetUrlFilter : ILiquidFilter { - public class AssetUrlFilter : ILiquidFilter - { - private readonly IMediaFileStore _mediaFileStore; + private readonly IMediaFileStore _mediaFileStore; - public AssetUrlFilter(IMediaFileStore mediaFileStore) - { - _mediaFileStore = mediaFileStore; - } + public AssetUrlFilter(IMediaFileStore mediaFileStore) + { + _mediaFileStore = mediaFileStore; + } - public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) - { - var url = input.ToStringValue(); - var imageUrl = _mediaFileStore.MapPathToPublicUrl(url); - return new ValueTask(new StringValue(imageUrl ?? url)); - } + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + { + var url = input.ToStringValue(); + var imageUrl = _mediaFileStore.MapPathToPublicUrl(url); + return new ValueTask(new StringValue(imageUrl ?? url)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Filters/MediaFilters.cs b/src/OrchardCore.Modules/OrchardCore.Media/Filters/MediaFilters.cs index d48873a49fe..22cb2b5f087 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Filters/MediaFilters.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Filters/MediaFilters.cs @@ -2,24 +2,23 @@ using Fluid; using Fluid.Values; -namespace OrchardCore.Media.Filters +namespace OrchardCore.Media.Filters; + +public static class MediaFilters { - public static class MediaFilters + public static ValueTask ImgTag(FluidValue input, FilterArguments arguments, TemplateContext _) { - public static ValueTask ImgTag(FluidValue input, FilterArguments arguments, TemplateContext _) - { - var url = input.ToStringValue(); + var url = input.ToStringValue(); - var imgTag = $"(new StringValue(imgTag) { Encode = false }); - } + return new ValueTask(new StringValue(imgTag) { Encode = false }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Filters/ResizeUrlFilter.cs b/src/OrchardCore.Modules/OrchardCore.Media/Filters/ResizeUrlFilter.cs index 210aaff6cc4..43db52e3322 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Filters/ResizeUrlFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Filters/ResizeUrlFilter.cs @@ -11,124 +11,123 @@ using OrchardCore.Media.Fields; using OrchardCore.Media.Services; -namespace OrchardCore.Media.Filters +namespace OrchardCore.Media.Filters; + +public class ResizeUrlFilter : ILiquidFilter { - public class ResizeUrlFilter : ILiquidFilter + private readonly IMediaProfileService _mediaProfileService; + private readonly IMediaTokenService _mediaTokenService; + private readonly MediaOptions _options; + + public ResizeUrlFilter(IMediaProfileService mediaProfileService, IMediaTokenService mediaTokenService, IOptions options) { - private readonly IMediaProfileService _mediaProfileService; - private readonly IMediaTokenService _mediaTokenService; - private readonly MediaOptions _options; + _mediaProfileService = mediaProfileService; + _mediaTokenService = mediaTokenService; + _options = options.Value; + } - public ResizeUrlFilter(IMediaProfileService mediaProfileService, IMediaTokenService mediaTokenService, IOptions options) - { - _mediaProfileService = mediaProfileService; - _mediaTokenService = mediaTokenService; - _options = options.Value; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) + { + var url = input.ToStringValue(); - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) - { - var url = input.ToStringValue(); + // Profile is a named argument only. + var profile = arguments["profile"]; - // Profile is a named argument only. - var profile = arguments["profile"]; + IDictionary queryStringParams; - IDictionary queryStringParams; + if (!profile.IsNil()) + { + queryStringParams = await _mediaProfileService.GetMediaProfileCommands(profile.ToStringValue()); + + // Additional commands to a profile must be named. + var width = arguments["width"]; + var height = arguments["height"]; + var mode = arguments["mode"]; + var quality = arguments["quality"]; + var format = arguments["format"]; + var anchor = arguments["anchor"]; + var bgcolor = arguments["bgcolor"]; + + ApplyQueryStringParams(queryStringParams, width, height, mode, quality, format, anchor, bgcolor); + } + else + { + queryStringParams = new Dictionary(); - if (!profile.IsNil()) - { - queryStringParams = await _mediaProfileService.GetMediaProfileCommands(profile.ToStringValue()); - - // Additional commands to a profile must be named. - var width = arguments["width"]; - var height = arguments["height"]; - var mode = arguments["mode"]; - var quality = arguments["quality"]; - var format = arguments["format"]; - var anchor = arguments["anchor"]; - var bgcolor = arguments["bgcolor"]; - - ApplyQueryStringParams(queryStringParams, width, height, mode, quality, format, anchor, bgcolor); - } - else - { - queryStringParams = new Dictionary(); + // Never mix named and indexed arguments as this leads to unpredictable results. + var useNamed = arguments.Names.Any(); - // Never mix named and indexed arguments as this leads to unpredictable results. - var useNamed = arguments.Names.Any(); + var width = useNamed ? arguments["width"] : arguments.At(0); + var height = useNamed ? arguments["height"] : arguments.At(1); + var mode = useNamed ? arguments["mode"] : arguments.At(2); + var quality = useNamed ? arguments["quality"] : arguments.At(3); + var format = useNamed ? arguments["format"] : arguments.At(4); + var anchor = useNamed ? arguments["anchor"] : arguments.At(5); + var bgcolor = useNamed ? arguments["bgcolor"] : arguments.At(6); - var width = useNamed ? arguments["width"] : arguments.At(0); - var height = useNamed ? arguments["height"] : arguments.At(1); - var mode = useNamed ? arguments["mode"] : arguments.At(2); - var quality = useNamed ? arguments["quality"] : arguments.At(3); - var format = useNamed ? arguments["format"] : arguments.At(4); - var anchor = useNamed ? arguments["anchor"] : arguments.At(5); - var bgcolor = useNamed ? arguments["bgcolor"] : arguments.At(6); + ApplyQueryStringParams(queryStringParams, width, height, mode, quality, format, anchor, bgcolor); + } - ApplyQueryStringParams(queryStringParams, width, height, mode, quality, format, anchor, bgcolor); - } + var resizedUrl = QueryHelpers.AddQueryString(url, queryStringParams); - var resizedUrl = QueryHelpers.AddQueryString(url, queryStringParams); + if (_options.UseTokenizedQueryString) + { + resizedUrl = _mediaTokenService.AddTokenToPath(resizedUrl); + } - if (_options.UseTokenizedQueryString) - { - resizedUrl = _mediaTokenService.AddTokenToPath(resizedUrl); - } + return new StringValue(resizedUrl); + } - return new StringValue(resizedUrl); + private static void ApplyQueryStringParams(IDictionary queryStringParams, FluidValue width, FluidValue height, FluidValue mode, FluidValue quality, FluidValue format, FluidValue anchorValue, FluidValue bgcolor) + { + if (!width.IsNil()) + { + queryStringParams["width"] = width.ToStringValue(); } - private static void ApplyQueryStringParams(IDictionary queryStringParams, FluidValue width, FluidValue height, FluidValue mode, FluidValue quality, FluidValue format, FluidValue anchorValue, FluidValue bgcolor) + if (!height.IsNil()) { - if (!width.IsNil()) - { - queryStringParams["width"] = width.ToStringValue(); - } + queryStringParams["height"] = height.ToStringValue(); + } - if (!height.IsNil()) - { - queryStringParams["height"] = height.ToStringValue(); - } + if (!mode.IsNil()) + { + queryStringParams["rmode"] = mode.ToStringValue(); + } - if (!mode.IsNil()) - { - queryStringParams["rmode"] = mode.ToStringValue(); - } + if (!quality.IsNil()) + { + queryStringParams["quality"] = quality.ToStringValue(); + } - if (!quality.IsNil()) - { - queryStringParams["quality"] = quality.ToStringValue(); - } + if (!format.IsNil()) + { + queryStringParams["format"] = format.ToStringValue(); + } - if (!format.IsNil()) - { - queryStringParams["format"] = format.ToStringValue(); - } + if (!anchorValue.IsNil()) + { + var obj = anchorValue.ToObjectValue(); - if (!anchorValue.IsNil()) + if (obj is not Anchor anchor) { - var obj = anchorValue.ToObjectValue(); + anchor = null; - if (obj is not Anchor anchor) + if (obj is JsonObject jObject) { - anchor = null; - - if (obj is JsonObject jObject) - { - anchor = jObject.ToObject(); - } - } - if (anchor != null) - { - queryStringParams["rxy"] = anchor.X.ToString(CultureInfo.InvariantCulture) + ',' + anchor.Y.ToString(CultureInfo.InvariantCulture); + anchor = jObject.ToObject(); } } - - if (!bgcolor.IsNil()) + if (anchor != null) { - queryStringParams["bgcolor"] = bgcolor.ToStringValue(); + queryStringParams["rxy"] = anchor.X.ToString(CultureInfo.InvariantCulture) + ',' + anchor.Y.ToString(CultureInfo.InvariantCulture); } } + + if (!bgcolor.IsNil()) + { + queryStringParams["bgcolor"] = bgcolor.ToStringValue(); + } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs index 467c5478e47..ec6caaa963e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetObjectType.cs @@ -2,29 +2,28 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.FileStorage; -namespace OrchardCore.Media.GraphQL +namespace OrchardCore.Media.GraphQL; + +public class MediaAssetObjectType : ObjectGraphType { - public class MediaAssetObjectType : ObjectGraphType + public MediaAssetObjectType() { - public MediaAssetObjectType() - { - Name = "MediaAsset"; + Name = "MediaAsset"; - Field(file => file.Name).Description("The name of the asset."); + Field(file => file.Name).Description("The name of the asset."); - Field("path") - .Description("The url to the asset.") - .Resolve(x => - { - var path = x.Source.Path; - var mediaFileStore = x.RequestServices.GetService(); - return mediaFileStore.MapPathToPublicUrl(path); - }); + Field("path") + .Description("The url to the asset.") + .Resolve(x => + { + var path = x.Source.Path; + var mediaFileStore = x.RequestServices.GetService(); + return mediaFileStore.MapPathToPublicUrl(path); + }); - Field(file => file.Length).Description("The length of the file."); - Field("lastModifiedUtc") - .Description("The date and time in UTC when the asset was last modified.") - .Resolve(file => file.Source.LastModifiedUtc); - } + Field(file => file.Length).Description("The length of the file."); + Field("lastModifiedUtc") + .Description("The date and time in UTC when the asset was last modified.") + .Resolve(file => file.Source.LastModifiedUtc); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetQuery.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetQuery.cs index 0880bed5c83..b8a19c5b011 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetQuery.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaAssetQuery.cs @@ -11,72 +11,71 @@ using OrchardCore.ContentManagement.GraphQL.Options; using OrchardCore.FileStorage; -namespace OrchardCore.Media.GraphQL +namespace OrchardCore.Media.GraphQL; + +public class MediaAssetQuery : ISchemaBuilder { - public class MediaAssetQuery : ISchemaBuilder + protected readonly IStringLocalizer S; + private readonly GraphQLContentOptions _graphQLContentOptions; + + public MediaAssetQuery( + IStringLocalizer localizer, + IOptions graphQLContentOptions) { - protected readonly IStringLocalizer S; - private readonly GraphQLContentOptions _graphQLContentOptions; + S = localizer; + _graphQLContentOptions = graphQLContentOptions.Value; + } + + public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - public MediaAssetQuery( - IStringLocalizer localizer, - IOptions graphQLContentOptions) + public Task BuildAsync(ISchema schema) + { + if (_graphQLContentOptions.IsHiddenByDefault("MediaAssets")) { - S = localizer; - _graphQLContentOptions = graphQLContentOptions.Value; + return Task.CompletedTask; } - public Task GetIdentifierAsync() => Task.FromResult(string.Empty); - - public Task BuildAsync(ISchema schema) + var field = new FieldType { - if (_graphQLContentOptions.IsHiddenByDefault("MediaAssets")) - { - return Task.CompletedTask; - } - - var field = new FieldType - { - Name = "MediaAssets", - Description = S["Media assets are items that are part of your media library."], - Type = typeof(ListGraphType), - Arguments = new QueryArguments( - new QueryArgument - { - Name = "path", - Description = S["Media asset path."] - }, - new QueryArgument - { - Name = "includeSubDirectories", - Description = S["Whether to get the assets from just the top directory or from all sub-directories as well."] - } - ), - Resolver = new LockedAsyncFieldResolver>(ResolveAsync) - }; + Name = "MediaAssets", + Description = S["Media assets are items that are part of your media library."], + Type = typeof(ListGraphType), + Arguments = new QueryArguments( + new QueryArgument + { + Name = "path", + Description = S["Media asset path."] + }, + new QueryArgument + { + Name = "includeSubDirectories", + Description = S["Whether to get the assets from just the top directory or from all sub-directories as well."] + } + ), + Resolver = new LockedAsyncFieldResolver>(ResolveAsync) + }; - schema.Query.AddField(field); + schema.Query.AddField(field); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) - { - var mediaFileStore = resolveContext.RequestServices.GetService(); + private async ValueTask> ResolveAsync(IResolveFieldContext resolveContext) + { + var mediaFileStore = resolveContext.RequestServices.GetService(); - var path = resolveContext.GetArgument("path", string.Empty); - var includeSubDirectories = resolveContext.GetArgument("includeSubDirectories", false); + var path = resolveContext.GetArgument("path", string.Empty); + var includeSubDirectories = resolveContext.GetArgument("includeSubDirectories", false); - var allFiles = mediaFileStore.GetDirectoryContentAsync(path, includeSubDirectories); + var allFiles = mediaFileStore.GetDirectoryContentAsync(path, includeSubDirectories); - if (includeSubDirectories) - { - return await allFiles.ToListAsync(); - } - else - { - return await allFiles.Where(x => !x.IsDirectory).ToListAsync(); - } + if (includeSubDirectories) + { + return await allFiles.ToListAsync(); + } + else + { + return await allFiles.Where(x => !x.IsDirectory).ToListAsync(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs index a09243a3c66..6a304c31aee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/MediaFieldQueryObjectType.cs @@ -6,118 +6,117 @@ using OrchardCore.Apis.GraphQL; using OrchardCore.Media.Fields; -namespace OrchardCore.Media.GraphQL +namespace OrchardCore.Media.GraphQL; + +public class MediaFieldQueryObjectType : ObjectGraphType { - public class MediaFieldQueryObjectType : ObjectGraphType + public MediaFieldQueryObjectType() { - public MediaFieldQueryObjectType() - { - Name = nameof(MediaField); - - if (MediaAppContextSwitches.EnableLegacyMediaFields) - { - Field, IEnumerable>("paths") - .Description("the media paths") - .PagingArguments() - .Resolve(x => - { - if (x.Source?.Paths is null) - { - return Array.Empty(); - } + Name = nameof(MediaField); - return x.Page(x.Source.Paths); - }); - - Field, IEnumerable>("fileNames") - .Description("the media file names") - .PagingArguments() - .Resolve(x => - { - var fileNames = x.Page(x.Source.GetAttachedFileNames()); - if (fileNames is null) - { - return Array.Empty(); - } - return fileNames; - }); - - Field, IEnumerable>("urls") - .Description("the absolute urls of the media items") - .PagingArguments() - .Resolve(x => + if (MediaAppContextSwitches.EnableLegacyMediaFields) + { + Field, IEnumerable>("paths") + .Description("the media paths") + .PagingArguments() + .Resolve(x => + { + if (x.Source?.Paths is null) { - if (x.Source?.Paths is null) - { - return Array.Empty(); - } - var paths = x.Page(x.Source.Paths); - var mediaFileStore = x.RequestServices.GetService(); + return Array.Empty(); + } - return paths.Select(p => mediaFileStore.MapPathToPublicUrl(p)); - }); + return x.Page(x.Source.Paths); + }); - Field, IEnumerable>("mediatexts") - .Description("the media texts") + Field, IEnumerable>("fileNames") + .Description("the media file names") .PagingArguments() .Resolve(x => { - if (x.Source?.MediaTexts is null) + var fileNames = x.Page(x.Source.GetAttachedFileNames()); + if (fileNames is null) { return Array.Empty(); } - return x.Page(x.Source.MediaTexts); + return fileNames; }); - } - Field, IEnumerable>("files") - .Description("the files of the media items") + Field, IEnumerable>("urls") + .Description("the absolute urls of the media items") .PagingArguments() .Resolve(x => { if (x.Source?.Paths is null) { - return []; + return Array.Empty(); } - - var paths = x.Page(x.Source.Paths).ToArray(); + var paths = x.Page(x.Source.Paths); var mediaFileStore = x.RequestServices.GetService(); - var urls = paths.Select(p => mediaFileStore.MapPathToPublicUrl(p)).ToArray(); - var fileNames = x.Page(x.Source?.GetAttachedFileNames()).ToArray(); - var items = new List(); - var mediaTexts = x.Source?.MediaTexts ?? []; - for (int i = 0; i < paths.Length; i++) - { - items.Add(new MediaFileItem - { - Path = paths[i], - FileName = fileNames.Length > i ? fileNames[i] : string.Empty, - Url = urls.Length > i ? urls[i] : string.Empty, - MediaText = mediaTexts.Length > i ? mediaTexts[i] : string.Empty, - }); - } - return items; + return paths.Select(p => mediaFileStore.MapPathToPublicUrl(p)); }); - } - } - public sealed class MediaFileItemType : ObjectGraphType - { - public MediaFileItemType() - { - Field("fileName").Description("the file name of the media file item").Resolve(x => x.Source.FileName); - Field("path").Description("the path of the media file item").Resolve(x => x.Source.Path); - Field("url").Description("the url name of the media file item").Resolve(x => x.Source.Url); - Field("mediaText").Description("the media text of the file item").Resolve(x => x.Source.MediaText); + Field, IEnumerable>("mediatexts") + .Description("the media texts") + .PagingArguments() + .Resolve(x => + { + if (x.Source?.MediaTexts is null) + { + return Array.Empty(); + } + return x.Page(x.Source.MediaTexts); + }); } + + Field, IEnumerable>("files") + .Description("the files of the media items") + .PagingArguments() + .Resolve(x => + { + if (x.Source?.Paths is null) + { + return []; + } + + var paths = x.Page(x.Source.Paths).ToArray(); + var mediaFileStore = x.RequestServices.GetService(); + var urls = paths.Select(p => mediaFileStore.MapPathToPublicUrl(p)).ToArray(); + var fileNames = x.Page(x.Source?.GetAttachedFileNames()).ToArray(); + var items = new List(); + var mediaTexts = x.Source?.MediaTexts ?? []; + for (int i = 0; i < paths.Length; i++) + { + items.Add(new MediaFileItem + { + Path = paths[i], + FileName = fileNames.Length > i ? fileNames[i] : string.Empty, + Url = urls.Length > i ? urls[i] : string.Empty, + MediaText = mediaTexts.Length > i ? mediaTexts[i] : string.Empty, + }); + } + + return items; + }); } +} - public sealed class MediaFileItem +public sealed class MediaFileItemType : ObjectGraphType +{ + public MediaFileItemType() { - public string FileName { get; set; } - public string Path { get; set; } - public string Url { get; set; } - public string MediaText { get; set; } + Field("fileName").Description("the file name of the media file item").Resolve(x => x.Source.FileName); + Field("path").Description("the path of the media file item").Resolve(x => x.Source.Path); + Field("url").Description("the url name of the media file item").Resolve(x => x.Source.Url); + Field("mediaText").Description("the media text of the file item").Resolve(x => x.Source.MediaText); } } + +public sealed class MediaFileItem +{ + public string FileName { get; set; } + public string Path { get; set; } + public string Url { get; set; } + public string MediaText { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/Startup.cs index 5848a96f0c6..7519e3f41fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/GraphQL/Startup.cs @@ -4,16 +4,15 @@ using OrchardCore.Media.Fields; using OrchardCore.Modules; -namespace OrchardCore.Media.GraphQL +namespace OrchardCore.Media.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddObjectGraphType(); - services.AddTransient(); - } + services.AddSingleton(); + services.AddObjectGraphType(); + services.AddTransient(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Handlers/AttachedMediaFieldContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Media/Handlers/AttachedMediaFieldContentHandler.cs index d3d8dc9821e..422c8b591c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Handlers/AttachedMediaFieldContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Handlers/AttachedMediaFieldContentHandler.cs @@ -2,26 +2,25 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.Media.Services; -namespace OrchardCore.Media.Handlers +namespace OrchardCore.Media.Handlers; + +/// +/// Content Handler to delete files used on attached media fields once the content item is deleted. +/// +public class AttachedMediaFieldContentHandler : ContentHandlerBase { - /// - /// Content Handler to delete files used on attached media fields once the content item is deleted. - /// - public class AttachedMediaFieldContentHandler : ContentHandlerBase - { - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - public AttachedMediaFieldContentHandler(AttachedMediaFieldFileService attachedMediaFieldFileService) - { - _attachedMediaFieldFileService = attachedMediaFieldFileService; - } + public AttachedMediaFieldContentHandler(AttachedMediaFieldFileService attachedMediaFieldFileService) + { + _attachedMediaFieldFileService = attachedMediaFieldFileService; + } - public override async Task RemovedAsync(RemoveContentContext context) + public override async Task RemovedAsync(RemoveContentContext context) + { + if (context.NoActiveVersionLeft) { - if (context.NoActiveVersionLeft) - { - await _attachedMediaFieldFileService.DeleteContentItemFolderAsync(context.ContentItem); - } + await _attachedMediaFieldFileService.DeleteContentItemFolderAsync(context.ContentItem); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Indexing/MediaFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Media/Indexing/MediaFieldIndexHandler.cs index 37859eee99c..60fdb758e96 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Indexing/MediaFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Indexing/MediaFieldIndexHandler.cs @@ -9,93 +9,92 @@ using OrchardCore.Media.Fields; using OrchardCore.Media.Settings; -namespace OrchardCore.Media.Indexing +namespace OrchardCore.Media.Indexing; + +public class MediaFieldIndexHandler : ContentFieldIndexHandler { - public class MediaFieldIndexHandler : ContentFieldIndexHandler - { - private const string MediaTextKeySuffix = ".MediaText"; - private const string FileTextKeySuffix = ".FileText"; + private const string MediaTextKeySuffix = ".MediaText"; + private const string FileTextKeySuffix = ".FileText"; - private readonly IMediaFileStore _mediaFileStore; - private readonly MediaFileIndexingOptions _mediaFileIndexingOptions; - private readonly IServiceProvider _serviceProvider; + private readonly IMediaFileStore _mediaFileStore; + private readonly MediaFileIndexingOptions _mediaFileIndexingOptions; + private readonly IServiceProvider _serviceProvider; - public MediaFieldIndexHandler( - IMediaFileStore mediaFileStore, - IOptions mediaFileIndexingOptions, - IServiceProvider serviceProvider) - { - _mediaFileStore = mediaFileStore; - _mediaFileIndexingOptions = mediaFileIndexingOptions.Value; - _serviceProvider = serviceProvider; - } + public MediaFieldIndexHandler( + IMediaFileStore mediaFileStore, + IOptions mediaFileIndexingOptions, + IServiceProvider serviceProvider) + { + _mediaFileStore = mediaFileStore; + _mediaFileIndexingOptions = mediaFileIndexingOptions.Value; + _serviceProvider = serviceProvider; + } - public async override Task BuildIndexAsync(MediaField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - var settings = context.ContentPartFieldDefinition.GetSettings(); + public async override Task BuildIndexAsync(MediaField field, BuildFieldIndexContext context) + { + var options = context.Settings.ToOptions(); + var settings = context.ContentPartFieldDefinition.GetSettings(); - if (field.Paths?.Length is null || field.Paths.Length == 0) + if (field.Paths?.Length is null || field.Paths.Length == 0) + { + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key + MediaTextKeySuffix, IndexingConstants.NullValue, options); - context.DocumentIndex.Set(key + FileTextKeySuffix, IndexingConstants.NullValue, options); - } - - return; + context.DocumentIndex.Set(key + MediaTextKeySuffix, IndexingConstants.NullValue, options); + context.DocumentIndex.Set(key + FileTextKeySuffix, IndexingConstants.NullValue, options); } - if (settings.AllowMediaText) + return; + } + + if (settings.AllowMediaText) + { + foreach (var key in context.Keys) { - foreach (var key in context.Keys) + if (field.MediaTexts != null) { - if (field.MediaTexts != null) - { - foreach (var mediaText in field.MediaTexts) - { - context.DocumentIndex.Set(key + MediaTextKeySuffix, mediaText, options); - } - } - else + foreach (var mediaText in field.MediaTexts) { - context.DocumentIndex.Set(key + MediaTextKeySuffix, IndexingConstants.NullValue, options); + context.DocumentIndex.Set(key + MediaTextKeySuffix, mediaText, options); } } + else + { + context.DocumentIndex.Set(key + MediaTextKeySuffix, IndexingConstants.NullValue, options); + } } + } - var paths = new HashSet(); + var paths = new HashSet(); - foreach (var path in field.Paths) + foreach (var path in field.Paths) + { + // The same file could be added several time to the field. + if (!paths.Add(path)) { - // The same file could be added several time to the field. - if (!paths.Add(path)) - { - // When a path is already processed, skip it. - continue; - } + // When a path is already processed, skip it. + continue; + } - var providerType = _mediaFileIndexingOptions.GetRegisteredMediaFileTextProvider(Path.GetExtension(path)); + var providerType = _mediaFileIndexingOptions.GetRegisteredMediaFileTextProvider(Path.GetExtension(path)); - if (providerType == null) - { - continue; - } + if (providerType == null) + { + continue; + } - using var fileStream = await _mediaFileStore.GetFileStreamAsync(path); + using var fileStream = await _mediaFileStore.GetFileStreamAsync(path); - if (fileStream == null) - { - continue; - } + if (fileStream == null) + { + continue; + } - var fileText = await _serviceProvider.CreateInstance(providerType) - .GetTextAsync(path, fileStream); + var fileText = await _serviceProvider.CreateInstance(providerType) + .GetTextAsync(path, fileStream); - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key + FileTextKeySuffix, fileText, options); - } + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key + FileTextKeySuffix, fileText, options); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Liquid/MediaAnchorTag.cs b/src/OrchardCore.Modules/OrchardCore.Media/Liquid/MediaAnchorTag.cs index cfa565023df..ca2d8615bd1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Liquid/MediaAnchorTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Liquid/MediaAnchorTag.cs @@ -11,91 +11,90 @@ using OrchardCore.DisplayManagement.Liquid.Tags; using OrchardCore.Liquid; -namespace OrchardCore.Media.Liquid +namespace OrchardCore.Media.Liquid; + +public class MediaAnchorTag : IAnchorTag { - public class MediaAnchorTag : IAnchorTag - { - public int Order => -20; + public int Order => -20; - public bool Match(IReadOnlyList argumentsList) + public bool Match(IReadOnlyList argumentsList) + { + foreach (var argument in argumentsList) { - foreach (var argument in argumentsList) + switch (argument.Name) { - switch (argument.Name) - { - case "asset-href": return true; - } + case "asset-href": return true; } - - return false; } - public async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, LiquidTemplateContext context) - { - var services = context.Services; - var mediaFileStore = services.GetRequiredService(); - var fileVersionProvider = services.GetRequiredService(); - var httpContextAccessor = services.GetRequiredService(); + return false; + } + + public async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, LiquidTemplateContext context) + { + var services = context.Services; + var mediaFileStore = services.GetRequiredService(); + var fileVersionProvider = services.GetRequiredService(); + var httpContextAccessor = services.GetRequiredService(); - string assetHref = null; - string appendVersion = null; + string assetHref = null; + string appendVersion = null; - Dictionary customAttributes = null; + Dictionary customAttributes = null; - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "asset_href": assetHref = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "asset_href": assetHref = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - default: + default: - customAttributes ??= []; - customAttributes[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); + customAttributes ??= []; + customAttributes[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); - break; - } + break; } + } - if (string.IsNullOrEmpty(assetHref)) - { - return Completion.Normal; - } + if (string.IsNullOrEmpty(assetHref)) + { + return Completion.Normal; + } - var resolvedUrl = mediaFileStore != null ? mediaFileStore.MapPathToPublicUrl(assetHref) : assetHref; + var resolvedUrl = mediaFileStore != null ? mediaFileStore.MapPathToPublicUrl(assetHref) : assetHref; - if (appendVersion != null && fileVersionProvider != null) - { - customAttributes["href"] = fileVersionProvider.AddFileVersionToPath(httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl); - } - else - { - customAttributes["href"] = resolvedUrl; - } + if (appendVersion != null && fileVersionProvider != null) + { + customAttributes["href"] = fileVersionProvider.AddFileVersionToPath(httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl); + } + else + { + customAttributes["href"] = resolvedUrl; + } - var tagBuilder = new TagBuilder("a"); + var tagBuilder = new TagBuilder("a"); - foreach (var attribute in customAttributes) - { - tagBuilder.Attributes[attribute.Key] = attribute.Value; - } + foreach (var attribute in customAttributes) + { + tagBuilder.Attributes[attribute.Key] = attribute.Value; + } - tagBuilder.RenderStartTag().WriteTo(writer, (HtmlEncoder)encoder); + tagBuilder.RenderStartTag().WriteTo(writer, (HtmlEncoder)encoder); - if (statements != null && statements.Count > 0) - { - var completion = await statements.RenderStatementsAsync(writer, encoder, context); + if (statements != null && statements.Count > 0) + { + var completion = await statements.RenderStatementsAsync(writer, encoder, context); - if (completion != Completion.Normal) - { - return completion; - } + if (completion != Completion.Normal) + { + return completion; } + } - tagBuilder.RenderEndTag().WriteTo(writer, (HtmlEncoder)encoder); + tagBuilder.RenderEndTag().WriteTo(writer, (HtmlEncoder)encoder); - return Completion.Normal; - } + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Media/Migrations.cs index b3ccfaaf070..6969657ce65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Migrations.cs @@ -6,36 +6,35 @@ using OrchardCore.Media.Fields; using OrchardCore.Media.Settings; -namespace OrchardCore.Media +namespace OrchardCore.Media; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ShellDescriptor _shellDescriptor; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ShellDescriptor _shellDescriptor; - public Migrations( - IContentDefinitionManager contentDefinitionManager, - ShellDescriptor shellDescriptor) - { - _contentDefinitionManager = contentDefinitionManager; - _shellDescriptor = shellDescriptor; - } + public Migrations( + IContentDefinitionManager contentDefinitionManager, + ShellDescriptor shellDescriptor) + { + _contentDefinitionManager = contentDefinitionManager; + _shellDescriptor = shellDescriptor; + } - // New installations don't need to be upgraded, but because there is no initial migration record, - // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. - public async Task CreateAsync() + // New installations don't need to be upgraded, but because there is no initial migration record, + // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. + public async Task CreateAsync() + { + if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.Media")) { - if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.Media")) - { - await UpgradeAsync(); - } - - // Shortcut other migration steps on new content definition schemas. - return 1; + await UpgradeAsync(); } - // Upgrade an existing installation. - private Task UpgradeAsync() - => _contentDefinitionManager.MigrateFieldSettingsAsync(); + // Shortcut other migration steps on new content definition schemas. + return 1; } + + // Upgrade an existing installation. + private Task UpgradeAsync() + => _contentDefinitionManager.MigrateFieldSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Models/MediaProfilesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Media/Models/MediaProfilesDocument.cs index 73701a468cd..2e916104119 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Models/MediaProfilesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Models/MediaProfilesDocument.cs @@ -3,21 +3,20 @@ using OrchardCore.Data.Documents; using OrchardCore.Media.Processing; -namespace OrchardCore.Media.Models +namespace OrchardCore.Media.Models; + +public class MediaProfilesDocument : Document { - public class MediaProfilesDocument : Document - { - public Dictionary MediaProfiles { get; init; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + public Dictionary MediaProfiles { get; init; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +} - public class MediaProfile - { - public string Hint { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public ResizeMode Mode { get; set; } - public Format Format { get; set; } - public int Quality { get; set; } - public string BackgroundColor { get; set; } - } +public class MediaProfile +{ + public string Hint { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public ResizeMode Mode { get; set; } + public Format Format { get; set; } + public int Quality { get; set; } + public string BackgroundColor { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/BackwardsCompatibleCacheKey.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/BackwardsCompatibleCacheKey.cs index 7ab6c0e45e6..a6bbb0e418e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/BackwardsCompatibleCacheKey.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/BackwardsCompatibleCacheKey.cs @@ -3,31 +3,30 @@ using SixLabors.ImageSharp.Web.Caching; using SixLabors.ImageSharp.Web.Commands; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +/// +/// Backwards compatible absolute url cache key. +/// +public class BackwardsCompatibleCacheKey : ICacheKey { - /// - /// Backwards compatible absolute url cache key. - /// - public class BackwardsCompatibleCacheKey : ICacheKey + /// + public string Create(HttpContext context, CommandCollection commands) { - /// - public string Create(HttpContext context, CommandCollection commands) - { - - var pathBase = context.Request.PathBase; - if (pathBase.HasValue) - { - // Due to bugs with an earlier version of cache calculation the cache key result was - // localhost:44300/agency1//media/portfolio/5.jpg?width=600&height=480&rmode=stretch - // the default ImageSharp absolute cache builder produces the correct value - // localhost:44300/agency1/media/portfolio/5.jpg?width=600&height=480&rmode=stretch - // which causes an entire cache refresh. - // refer https://github.com/SixLabors/ImageSharp.Web/issues/254 - pathBase = new PathString(pathBase + "//"); - } + var pathBase = context.Request.PathBase; + if (pathBase.HasValue) + { + // Due to bugs with an earlier version of cache calculation the cache key result was + // localhost:44300/agency1//media/portfolio/5.jpg?width=600&height=480&rmode=stretch + // the default ImageSharp absolute cache builder produces the correct value + // localhost:44300/agency1/media/portfolio/5.jpg?width=600&height=480&rmode=stretch + // which causes an entire cache refresh. + // refer https://github.com/SixLabors/ImageSharp.Web/issues/254 - return CaseHandlingUriBuilder.BuildAbsolute(CaseHandlingUriBuilder.CaseHandling.LowerInvariant, context.Request.Host, pathBase, context.Request.Path, QueryString.Create(commands)); + pathBase = new PathString(pathBase + "//"); } + + return CaseHandlingUriBuilder.BuildAbsolute(CaseHandlingUriBuilder.CaseHandling.LowerInvariant, context.Request.Host, pathBase, context.Request.Path, QueryString.Create(commands)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageSharpUrlFormatter.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageSharpUrlFormatter.cs index bd5e5ec26db..9bb607a2cc4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageSharpUrlFormatter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageSharpUrlFormatter.cs @@ -3,79 +3,78 @@ using Microsoft.AspNetCore.WebUtilities; using OrchardCore.Media.Fields; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +public enum ResizeMode { - public enum ResizeMode - { - Undefined, - Max, - Crop, - Pad, - BoxPad, - Min, - Stretch - } + Undefined, + Max, + Crop, + Pad, + BoxPad, + Min, + Stretch +} - public enum Format - { - Undefined, - Bmp, - Gif, - Jpg, - Png, - Tga, - WebP - } +public enum Format +{ + Undefined, + Bmp, + Gif, + Jpg, + Png, + Tga, + WebP +} - internal sealed class ImageSharpUrlFormatter +internal sealed class ImageSharpUrlFormatter +{ + public static string GetImageResizeUrl(string path, IDictionary queryStringParams = null, int? width = null, int? height = null, ResizeMode resizeMode = ResizeMode.Undefined, int? quality = null, Format format = Format.Undefined, Anchor anchor = null, string bgcolor = null) { - public static string GetImageResizeUrl(string path, IDictionary queryStringParams = null, int? width = null, int? height = null, ResizeMode resizeMode = ResizeMode.Undefined, int? quality = null, Format format = Format.Undefined, Anchor anchor = null, string bgcolor = null) + if (string.IsNullOrEmpty(path) || (!width.HasValue && !height.HasValue && queryStringParams == null)) { - if (string.IsNullOrEmpty(path) || (!width.HasValue && !height.HasValue && queryStringParams == null)) - { - return path; - } - - queryStringParams ??= new Dictionary(); + return path; + } - if (width.HasValue) - { - queryStringParams["width"] = width.ToString(); - } + queryStringParams ??= new Dictionary(); - if (height.HasValue) - { - queryStringParams["height"] = height.ToString(); - } + if (width.HasValue) + { + queryStringParams["width"] = width.ToString(); + } - if (resizeMode != ResizeMode.Undefined) - { - queryStringParams["rmode"] = resizeMode.ToString().ToLower(); - } + if (height.HasValue) + { + queryStringParams["height"] = height.ToString(); + } - // The format is set before quality such that the quality is not - // invalidated when the url is generated. - if (format != Format.Undefined) - { - queryStringParams["format"] = format.ToString().ToLower(); - } + if (resizeMode != ResizeMode.Undefined) + { + queryStringParams["rmode"] = resizeMode.ToString().ToLower(); + } - if (quality.HasValue) - { - queryStringParams["quality"] = quality.ToString(); - } + // The format is set before quality such that the quality is not + // invalidated when the url is generated. + if (format != Format.Undefined) + { + queryStringParams["format"] = format.ToString().ToLower(); + } - if (anchor != null) - { - queryStringParams["rxy"] = anchor.X.ToString(CultureInfo.InvariantCulture) + ',' + anchor.Y.ToString(CultureInfo.InvariantCulture); - } + if (quality.HasValue) + { + queryStringParams["quality"] = quality.ToString(); + } - if (!string.IsNullOrEmpty(bgcolor)) - { - queryStringParams["bgcolor"] = bgcolor; - } + if (anchor != null) + { + queryStringParams["rxy"] = anchor.X.ToString(CultureInfo.InvariantCulture) + ',' + anchor.Y.ToString(CultureInfo.InvariantCulture); + } - return QueryHelpers.AddQueryString(path, queryStringParams); + if (!string.IsNullOrEmpty(bgcolor)) + { + queryStringParams["bgcolor"] = bgcolor; } + + return QueryHelpers.AddQueryString(path, queryStringParams); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageVersionProcessor.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageVersionProcessor.cs index 6b939abbf81..7f9a754c8b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageVersionProcessor.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/ImageVersionProcessor.cs @@ -5,26 +5,25 @@ using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.Processors; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +/// +/// Pass through processor which allows inclusion of a version query string in the cache key. +/// +public class ImageVersionProcessor : IImageWebProcessor { /// - /// Pass through processor which allows inclusion of a version query string in the cache key. + /// The command constant for a version query string. /// - public class ImageVersionProcessor : IImageWebProcessor - { - /// - /// The command constant for a version query string. - /// - public const string VersionCommand = "v"; + public const string VersionCommand = "v"; - private static readonly IEnumerable _versionCommands = new[] { VersionCommand }; + private static readonly IEnumerable _versionCommands = new[] { VersionCommand }; - public IEnumerable Commands => _versionCommands; + public IEnumerable Commands => _versionCommands; - public FormattedImage Process(FormattedImage image, ILogger logger, CommandCollection commands, CommandParser parser, CultureInfo culture) - => image; + public FormattedImage Process(FormattedImage image, ILogger logger, CommandCollection commands, CommandParser parser, CultureInfo culture) + => image; - public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) - => false; - } + public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) + => false; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaImageSharpConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaImageSharpConfiguration.cs index 05e0415f0af..1e8ce70bda6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaImageSharpConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaImageSharpConfiguration.cs @@ -7,131 +7,130 @@ using SixLabors.ImageSharp.Web.Middleware; using SixLabors.ImageSharp.Web.Processors; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +/// +/// Provides default configuration for ImageSharp. +/// +public sealed class MediaImageSharpConfiguration : IConfigureOptions { - /// - /// Provides default configuration for ImageSharp. - /// - public sealed class MediaImageSharpConfiguration : IConfigureOptions - { - private readonly MediaOptions _mediaOptions; + private readonly MediaOptions _mediaOptions; - public MediaImageSharpConfiguration(IOptions mediaOptions) - { - _mediaOptions = mediaOptions.Value; - } + public MediaImageSharpConfiguration(IOptions mediaOptions) + { + _mediaOptions = mediaOptions.Value; + } - public void Configure(ImageSharpMiddlewareOptions options) + public void Configure(ImageSharpMiddlewareOptions options) + { + options.Configuration = Configuration.Default; + options.BrowserMaxAge = TimeSpan.FromDays(_mediaOptions.MaxBrowserCacheDays); + options.CacheMaxAge = TimeSpan.FromDays(_mediaOptions.MaxCacheDays); + options.CacheHashLength = 12; + options.OnParseCommandsAsync = context => { - options.Configuration = Configuration.Default; - options.BrowserMaxAge = TimeSpan.FromDays(_mediaOptions.MaxBrowserCacheDays); - options.CacheMaxAge = TimeSpan.FromDays(_mediaOptions.MaxCacheDays); - options.CacheHashLength = 12; - options.OnParseCommandsAsync = context => + if (context.Commands.Count == 0) { - if (context.Commands.Count == 0) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - var mediaOptions = context.Context.RequestServices.GetRequiredService>().Value; - if (mediaOptions.UseTokenizedQueryString) + var mediaOptions = context.Context.RequestServices.GetRequiredService>().Value; + if (mediaOptions.UseTokenizedQueryString) + { + if (context.Commands.TryGetValue(TokenCommandProcessor.TokenCommand, out var tokenString)) { - if (context.Commands.TryGetValue(TokenCommandProcessor.TokenCommand, out var tokenString)) - { - var mediaTokenService = context.Context.RequestServices.GetRequiredService(); - // The token must now be validated against the HMAC of the other commands. - // Use the Raw value, not the parsed value. - var token = context.Commands["token"]; + var mediaTokenService = context.Context.RequestServices.GetRequiredService(); + // The token must now be validated against the HMAC of the other commands. + // Use the Raw value, not the parsed value. + var token = context.Commands["token"]; - // Remove the token from the commands. - context.Commands.Remove(TokenCommandProcessor.TokenCommand); + // Remove the token from the commands. + context.Commands.Remove(TokenCommandProcessor.TokenCommand); - // When token is invalid no image commands will be processed. - if (!mediaTokenService.TryValidateToken(context.Commands, token)) - { - context.Commands.Clear(); - - return Task.CompletedTask; - } - - // Do not evaluate supported sizes here, as with tokenization any size is allowed. - } - else + // When token is invalid no image commands will be processed. + if (!mediaTokenService.TryValidateToken(context.Commands, token)) { - ValidateTokenlessCommands(context, mediaOptions); + context.Commands.Clear(); + + return Task.CompletedTask; } + + // Do not evaluate supported sizes here, as with tokenization any size is allowed. } else { ValidateTokenlessCommands(context, mediaOptions); } + } + else + { + ValidateTokenlessCommands(context, mediaOptions); + } - // The following commands are not supported by default. - context.Commands.Remove(ResizeWebProcessor.Compand); - context.Commands.Remove(ResizeWebProcessor.Sampler); - context.Commands.Remove(ResizeWebProcessor.Anchor); + // The following commands are not supported by default. + context.Commands.Remove(ResizeWebProcessor.Compand); + context.Commands.Remove(ResizeWebProcessor.Sampler); + context.Commands.Remove(ResizeWebProcessor.Anchor); - // When only a version command is applied pass on this request. - if (context.Commands.Count == 1 && context.Commands.Contains(ImageVersionProcessor.VersionCommand)) - { - context.Commands.Clear(); - } - else if (!context.Commands.Contains(ResizeWebProcessor.Mode)) - { - context.Commands[ResizeWebProcessor.Mode] = "max"; - } + // When only a version command is applied pass on this request. + if (context.Commands.Count == 1 && context.Commands.Contains(ImageVersionProcessor.VersionCommand)) + { + context.Commands.Clear(); + } + else if (!context.Commands.Contains(ResizeWebProcessor.Mode)) + { + context.Commands[ResizeWebProcessor.Mode] = "max"; + } - return Task.CompletedTask; - }; + return Task.CompletedTask; + }; - var onPrepareResponse = options.OnPrepareResponseAsync; - options.OnPrepareResponseAsync = async context => + var onPrepareResponse = options.OnPrepareResponseAsync; + options.OnPrepareResponseAsync = async context => + { + if (onPrepareResponse is not null) { - if (onPrepareResponse is not null) - { - await onPrepareResponse(context); - } + await onPrepareResponse(context); + } - // Override cache control for secure files - if (context.IsSecureMediaRequested()) - { - var mediaOptions = context.RequestServices.GetRequiredService>().Value; - var secureCacheControl = mediaOptions.MaxSecureFilesBrowserCacheDays == 0 - ? "no-store" - : "public, must-revalidate, max-age=" + TimeSpan.FromDays(mediaOptions.MaxSecureFilesBrowserCacheDays).TotalSeconds.ToString(); + // Override cache control for secure files + if (context.IsSecureMediaRequested()) + { + var mediaOptions = context.RequestServices.GetRequiredService>().Value; + var secureCacheControl = mediaOptions.MaxSecureFilesBrowserCacheDays == 0 + ? "no-store" + : "public, must-revalidate, max-age=" + TimeSpan.FromDays(mediaOptions.MaxSecureFilesBrowserCacheDays).TotalSeconds.ToString(); - context.Response.Headers.CacheControl = secureCacheControl; - } - }; - } + context.Response.Headers.CacheControl = secureCacheControl; + } + }; + } + + private static void ValidateTokenlessCommands(ImageCommandContext context, MediaOptions mediaOptions) + { + // The following commands are not supported without a tokenized query string. + context.Commands.Remove(ResizeWebProcessor.Xy); + context.Commands.Remove(ImageVersionProcessor.VersionCommand); + context.Commands.Remove(BackgroundColorWebProcessor.Color); - private static void ValidateTokenlessCommands(ImageCommandContext context, MediaOptions mediaOptions) + // Width and height must be part of the supported sizes array when tokenization is disabled. + if (context.Commands.TryGetValue(ResizeWebProcessor.Width, out var widthString)) { - // The following commands are not supported without a tokenized query string. - context.Commands.Remove(ResizeWebProcessor.Xy); - context.Commands.Remove(ImageVersionProcessor.VersionCommand); - context.Commands.Remove(BackgroundColorWebProcessor.Color); + var width = context.Parser.ParseValue(widthString, context.Culture); - // Width and height must be part of the supported sizes array when tokenization is disabled. - if (context.Commands.TryGetValue(ResizeWebProcessor.Width, out var widthString)) + if (Array.BinarySearch(mediaOptions.SupportedSizes, width) < 0) { - var width = context.Parser.ParseValue(widthString, context.Culture); - - if (Array.BinarySearch(mediaOptions.SupportedSizes, width) < 0) - { - context.Commands.Remove(ResizeWebProcessor.Width); - } + context.Commands.Remove(ResizeWebProcessor.Width); } + } - if (context.Commands.TryGetValue(ResizeWebProcessor.Height, out var heightString)) - { - var height = context.Parser.ParseValue(heightString, context.Culture); + if (context.Commands.TryGetValue(ResizeWebProcessor.Height, out var heightString)) + { + var height = context.Parser.ParseValue(heightString, context.Culture); - if (Array.BinarySearch(mediaOptions.SupportedSizes, height) < 0) - { - context.Commands.Remove(ResizeWebProcessor.Height); - } + if (Array.BinarySearch(mediaOptions.SupportedSizes, height) < 0) + { + context.Commands.Remove(ResizeWebProcessor.Height); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaResizingFileProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaResizingFileProvider.cs index 1d0eba6f35d..36606ef452b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaResizingFileProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaResizingFileProvider.cs @@ -9,69 +9,68 @@ using SixLabors.ImageSharp.Web.Providers; using SixLabors.ImageSharp.Web.Resolvers; -namespace OrchardCore.Media.Processing -{ - public class MediaResizingFileProvider : IImageProvider - { - private readonly IMediaFileProvider _mediaFileProvider; - private readonly FormatUtilities _formatUtilities; - private readonly PathString _assetsRequestPath; +namespace OrchardCore.Media.Processing; - /// - /// A match function used by the resolver to identify itself as the correct resolver to use. - /// - private Func _match; +public class MediaResizingFileProvider : IImageProvider +{ + private readonly IMediaFileProvider _mediaFileProvider; + private readonly FormatUtilities _formatUtilities; + private readonly PathString _assetsRequestPath; - public MediaResizingFileProvider( - IMediaFileProvider mediaFileProvider, - IOptions imageSharpOptions, - IOptions mediaOptions - ) - { - _mediaFileProvider = mediaFileProvider; - _formatUtilities = new FormatUtilities(imageSharpOptions); - _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; - } + /// + /// A match function used by the resolver to identify itself as the correct resolver to use. + /// + private Func _match; - /// - public Func Match - { - get => _match ?? IsMatch; - set => _match = value; - } + public MediaResizingFileProvider( + IMediaFileProvider mediaFileProvider, + IOptions imageSharpOptions, + IOptions mediaOptions + ) + { + _mediaFileProvider = mediaFileProvider; + _formatUtilities = new FormatUtilities(imageSharpOptions); + _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; + } - public ProcessingBehavior ProcessingBehavior => ProcessingBehavior.CommandOnly; + /// + public Func Match + { + get => _match ?? IsMatch; + set => _match = value; + } - /// - public bool IsValidRequest(HttpContext context) - => _formatUtilities.TryGetExtensionFromUri(context.Request.GetDisplayUrl(), out _); + public ProcessingBehavior ProcessingBehavior => ProcessingBehavior.CommandOnly; - /// - public Task GetAsync(HttpContext context) - { - // Remove assets request path. - var path = context.Request.Path.Value[_assetsRequestPath.Value.Length..]; + /// + public bool IsValidRequest(HttpContext context) + => _formatUtilities.TryGetExtensionFromUri(context.Request.GetDisplayUrl(), out _); - var fileInfo = _mediaFileProvider.GetFileInfo(path); + /// + public Task GetAsync(HttpContext context) + { + // Remove assets request path. + var path = context.Request.Path.Value[_assetsRequestPath.Value.Length..]; - // Check to see if the file exists. - if (!fileInfo.Exists) - { - return Task.FromResult(null); - } + var fileInfo = _mediaFileProvider.GetFileInfo(path); - // We don't care about the content type nor cache control max age here. - return Task.FromResult(new FileProviderImageResolver(fileInfo)); + // Check to see if the file exists. + if (!fileInfo.Exists) + { + return Task.FromResult(null); } - private bool IsMatch(HttpContext context) - { - if (!context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase)) - { - return false; - } + // We don't care about the content type nor cache control max age here. + return Task.FromResult(new FileProviderImageResolver(fileInfo)); + } - return true; + private bool IsMatch(HttpContext context) + { + if (!context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase)) + { + return false; } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptions.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptions.cs index 9c70b1fec53..1053b393ac5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptions.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +public class MediaTokenOptions { - public class MediaTokenOptions - { - public byte[] HashKey { get; set; } - } + public byte[] HashKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptionsConfiguration.cs index c2a0c1e23ad..3aef8ffb000 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenOptionsConfiguration.cs @@ -1,23 +1,22 @@ using Microsoft.Extensions.Options; using OrchardCore.Settings; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +public sealed class MediaTokenOptionsConfiguration : IConfigureOptions { - public sealed class MediaTokenOptionsConfiguration : IConfigureOptions - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public MediaTokenOptionsConfiguration(ISiteService siteService) - { - _siteService = siteService; - } + public MediaTokenOptionsConfiguration(ISiteService siteService) + { + _siteService = siteService; + } - public void Configure(MediaTokenOptions options) - { - options.HashKey = _siteService.GetSettingsAsync() - .GetAwaiter() - .GetResult() - .HashKey; - } + public void Configure(MediaTokenOptions options) + { + options.HashKey = _siteService.GetSettingsAsync() + .GetAwaiter() + .GetResult() + .HashKey; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenService.cs index 8e1726a799a..d4e313580b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenService.cs @@ -11,218 +11,217 @@ using Microsoft.Extensions.Primitives; using SixLabors.ImageSharp.Web.Processors; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +public class MediaTokenService : IMediaTokenService { - public class MediaTokenService : IMediaTokenService - { - private const string TokenCacheKeyPrefix = "MediaToken:"; - private readonly IMemoryCache _memoryCache; + private const string TokenCacheKeyPrefix = "MediaToken:"; + private readonly IMemoryCache _memoryCache; - private readonly HashSet _knownCommands = new(12); - private readonly byte[] _hashKey; + private readonly HashSet _knownCommands = new(12); + private readonly byte[] _hashKey; - public MediaTokenService( - IMemoryCache memoryCache, - IOptions options, - IEnumerable processors) - { - _memoryCache = memoryCache; + public MediaTokenService( + IMemoryCache memoryCache, + IOptions options, + IEnumerable processors) + { + _memoryCache = memoryCache; - foreach (var processor in processors) + foreach (var processor in processors) + { + foreach (var command in processor.Commands) { - foreach (var command in processor.Commands) - { - _knownCommands.Add(command); - } + _knownCommands.Add(command); } + } + + _hashKey = options.Value.HashKey; + } - _hashKey = options.Value.HashKey; + public string AddTokenToPath(string path) + { + var pathIndex = path.IndexOf('?'); + + Dictionary processingCommands = null; + Dictionary otherCommands = null; + + if (pathIndex != -1) + { + ParseQuery(path[(pathIndex + 1)..], out processingCommands, out otherCommands); } - public string AddTokenToPath(string path) + // If no commands or only a version command don't bother tokenizing. + if (processingCommands is null + || processingCommands.Count == 0 + || processingCommands.Count == 1 && processingCommands.ContainsKey(ImageVersionProcessor.VersionCommand)) { - var pathIndex = path.IndexOf('?'); + return path; + } - Dictionary processingCommands = null; - Dictionary otherCommands = null; + // Using the command values as a key retrieve from cache. + var queryStringTokenKey = CreateQueryStringTokenKey(processingCommands); + var queryStringToken = GetHash(queryStringTokenKey); - if (pathIndex != -1) - { - ParseQuery(path[(pathIndex + 1)..], out processingCommands, out otherCommands); - } + processingCommands["token"] = queryStringToken; - // If no commands or only a version command don't bother tokenizing. - if (processingCommands is null - || processingCommands.Count == 0 - || processingCommands.Count == 1 && processingCommands.ContainsKey(ImageVersionProcessor.VersionCommand)) + // If any non-resizing parameters have been added to the path include these on the query string output. + if (otherCommands != null) + { + foreach (var command in otherCommands) { - return path; + processingCommands.Add(command.Key, command.Value); } + } - // Using the command values as a key retrieve from cache. - var queryStringTokenKey = CreateQueryStringTokenKey(processingCommands); - var queryStringToken = GetHash(queryStringTokenKey); - - processingCommands["token"] = queryStringToken; + return AddQueryString(path.AsSpan(0, pathIndex), processingCommands); + } - // If any non-resizing parameters have been added to the path include these on the query string output. - if (otherCommands != null) - { - foreach (var command in otherCommands) - { - processingCommands.Add(command.Key, command.Value); - } - } + private void ParseQuery( + string queryString, + out Dictionary processingCommands, + out Dictionary otherCommands) + { + processingCommands = null; + otherCommands = null; - return AddQueryString(path.AsSpan(0, pathIndex), processingCommands); - } + var accumulator = new KeyValueAccumulator(); + var otherCommandAccumulator = new KeyValueAccumulator(); + var enumerable = new QueryStringEnumerable(queryString); - private void ParseQuery( - string queryString, - out Dictionary processingCommands, - out Dictionary otherCommands) + foreach (var pair in enumerable) { - processingCommands = null; - otherCommands = null; + var key = pair.DecodeName().ToString(); + var value = pair.DecodeValue().ToString(); - var accumulator = new KeyValueAccumulator(); - var otherCommandAccumulator = new KeyValueAccumulator(); - var enumerable = new QueryStringEnumerable(queryString); - - foreach (var pair in enumerable) + if (_knownCommands.Contains(key)) { - var key = pair.DecodeName().ToString(); - var value = pair.DecodeValue().ToString(); - - if (_knownCommands.Contains(key)) - { - accumulator.Append(key, value); - } - else - { - otherCommandAccumulator.Append(key, value); - } + accumulator.Append(key, value); } - - if (accumulator.HasValues) + else { - processingCommands = accumulator.GetResults(); + otherCommandAccumulator.Append(key, value); } + } - if (otherCommandAccumulator.HasValues) - { - otherCommands = otherCommandAccumulator.GetResults(); - } + if (accumulator.HasValues) + { + processingCommands = accumulator.GetResults(); } - public bool TryValidateToken(KeyedCollection> commands, string token) + if (otherCommandAccumulator.HasValues) { - var queryStringTokenKey = CreateCommandCollectionTokenKey(commands); + otherCommands = otherCommandAccumulator.GetResults(); + } + } - // Store a hash of the valid query string commands. - var queryStringToken = GetHash(queryStringTokenKey); + public bool TryValidateToken(KeyedCollection> commands, string token) + { + var queryStringTokenKey = CreateCommandCollectionTokenKey(commands); - if (string.Equals(queryStringToken, token, StringComparison.OrdinalIgnoreCase)) - { - return true; - } + // Store a hash of the valid query string commands. + var queryStringToken = GetHash(queryStringTokenKey); - return false; + if (string.Equals(queryStringToken, token, StringComparison.OrdinalIgnoreCase)) + { + return true; } - /// - /// Specialized version with fast enumeration and no StringValues string allocation. - /// - private static string CreateQueryStringTokenKey(Dictionary values) - { - using var builder = ZString.CreateStringBuilder(); - builder.Append(TokenCacheKeyPrefix); - foreach (var pair in values) - { - builder.Append(pair.Value.ToString()); - } + return false; + } - return builder.ToString(); + /// + /// Specialized version with fast enumeration and no StringValues string allocation. + /// + private static string CreateQueryStringTokenKey(Dictionary values) + { + using var builder = ZString.CreateStringBuilder(); + builder.Append(TokenCacheKeyPrefix); + foreach (var pair in values) + { + builder.Append(pair.Value.ToString()); } - private static string CreateCommandCollectionTokenKey(KeyedCollection> values) - { - using var builder = ZString.CreateStringBuilder(); - builder.Append(TokenCacheKeyPrefix); - foreach (var pair in values) - { - builder.Append(pair.Value); - } + return builder.ToString(); + } - return builder.ToString(); + private static string CreateCommandCollectionTokenKey(KeyedCollection> values) + { + using var builder = ZString.CreateStringBuilder(); + builder.Append(TokenCacheKeyPrefix); + foreach (var pair in values) + { + builder.Append(pair.Value); } - private string GetHash(string queryStringTokenKey) - { - if (!_memoryCache.TryGetValue(queryStringTokenKey, out var result)) - { - using var entry = _memoryCache.CreateEntry(queryStringTokenKey); + return builder.ToString(); + } - entry.SlidingExpiration = TimeSpan.FromHours(5); + private string GetHash(string queryStringTokenKey) + { + if (!_memoryCache.TryGetValue(queryStringTokenKey, out var result)) + { + using var entry = _memoryCache.CreateEntry(queryStringTokenKey); - // 'queryStringTokenKey' also contains prefix. - var chars = queryStringTokenKey.AsSpan(TokenCacheKeyPrefix.Length); + entry.SlidingExpiration = TimeSpan.FromHours(5); - // Only allocate on stack if it's small enough. - var requiredLength = Encoding.UTF8.GetByteCount(chars); - var stringBytes = requiredLength < 1024 - ? stackalloc byte[requiredLength] - : new byte[requiredLength]; + // 'queryStringTokenKey' also contains prefix. + var chars = queryStringTokenKey.AsSpan(TokenCacheKeyPrefix.Length); - // 256 for SHA-256, fits in stack nicely. - Span hashBytes = stackalloc byte[HMACSHA256.HashSizeInBytes]; + // Only allocate on stack if it's small enough. + var requiredLength = Encoding.UTF8.GetByteCount(chars); + var stringBytes = requiredLength < 1024 + ? stackalloc byte[requiredLength] + : new byte[requiredLength]; - var stringBytesLength = Encoding.UTF8.GetBytes(chars, stringBytes); + // 256 for SHA-256, fits in stack nicely. + Span hashBytes = stackalloc byte[HMACSHA256.HashSizeInBytes]; - HMACSHA256.TryHashData(_hashKey, stringBytes[..stringBytesLength], hashBytes, out var hashBytesLength); + var stringBytesLength = Encoding.UTF8.GetBytes(chars, stringBytes); - entry.Value = result = Convert.ToBase64String(hashBytes[..hashBytesLength]); - } + HMACSHA256.TryHashData(_hashKey, stringBytes[..stringBytesLength], hashBytes, out var hashBytesLength); - return (string)result; + entry.Value = result = Convert.ToBase64String(hashBytes[..hashBytesLength]); } - /// - /// Custom version of that takes our pre-built - /// dictionary, uri as ReadOnlySpan<char> and uses ZString. Otherwise same logic. - /// - private static string AddQueryString( - ReadOnlySpan uri, - Dictionary queryString) - { - var anchorIndex = uri.IndexOf('#'); - var uriToBeAppended = uri; - var anchorText = ReadOnlySpan.Empty; - - // If there is an anchor, then the query string must be inserted before its first occurrence. - if (anchorIndex != -1) - { - anchorText = uri[anchorIndex..]; - uriToBeAppended = uri[..anchorIndex]; - } + return (string)result; + } - var queryIndex = uriToBeAppended.IndexOf('?'); - var hasQuery = queryIndex != -1; + /// + /// Custom version of that takes our pre-built + /// dictionary, uri as ReadOnlySpan<char> and uses ZString. Otherwise same logic. + /// + private static string AddQueryString( + ReadOnlySpan uri, + Dictionary queryString) + { + var anchorIndex = uri.IndexOf('#'); + var uriToBeAppended = uri; + var anchorText = ReadOnlySpan.Empty; - using var sb = ZString.CreateStringBuilder(); - sb.Append(uriToBeAppended); - foreach (var parameter in queryString) - { - sb.Append(hasQuery ? '&' : '?'); - sb.Append(UrlEncoder.Default.Encode(parameter.Key)); - sb.Append('='); - sb.Append(UrlEncoder.Default.Encode(parameter.Value)); - hasQuery = true; - } + // If there is an anchor, then the query string must be inserted before its first occurrence. + if (anchorIndex != -1) + { + anchorText = uri[anchorIndex..]; + uriToBeAppended = uri[..anchorIndex]; + } - sb.Append(anchorText); + var queryIndex = uriToBeAppended.IndexOf('?'); + var hasQuery = queryIndex != -1; - return sb.ToString(); + using var sb = ZString.CreateStringBuilder(); + sb.Append(uriToBeAppended); + foreach (var parameter in queryString) + { + sb.Append(hasQuery ? '&' : '?'); + sb.Append(UrlEncoder.Default.Encode(parameter.Key)); + sb.Append('='); + sb.Append(UrlEncoder.Default.Encode(parameter.Value)); + hasQuery = true; } + + sb.Append(anchorText); + + return sb.ToString(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettings.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettings.cs index 39a3885b56c..fcf3653b0c1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +public class MediaTokenSettings { - public class MediaTokenSettings - { - public byte[] HashKey { get; set; } - } + public byte[] HashKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettingsUpdater.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettingsUpdater.cs index a6e1880b8a8..71663de9518 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettingsUpdater.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/MediaTokenSettingsUpdater.cs @@ -7,67 +7,66 @@ using OrchardCore.Modules; using OrchardCore.Settings; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +/// +/// Generates a random key for media token hashing. +/// To regenerate the key disabled and enable the feature. +/// +public class MediaTokenSettingsUpdater : FeatureEventHandler, IModularTenantEvents { - /// - /// Generates a random key for media token hashing. - /// To regenerate the key disabled and enable the feature. - /// - public class MediaTokenSettingsUpdater : FeatureEventHandler, IModularTenantEvents - { - private const int DefaultMediaTokenKeySize = 64; + private const int DefaultMediaTokenKeySize = 64; - private readonly ISiteService _siteService; - private readonly ShellSettings _shellSettings; + private readonly ISiteService _siteService; + private readonly ShellSettings _shellSettings; - public MediaTokenSettingsUpdater(ISiteService siteService, ShellSettings shellSettings) - { - _siteService = siteService; - _shellSettings = shellSettings; - } + public MediaTokenSettingsUpdater(ISiteService siteService, ShellSettings shellSettings) + { + _siteService = siteService; + _shellSettings = shellSettings; + } - public async Task ActivatedAsync() + public async Task ActivatedAsync() + { + if (_shellSettings.IsUninitialized()) { - if (_shellSettings.IsUninitialized()) - { - // If the tenant is 'Uninitialized' there is no registered 'ISession' and then 'ISiteService' can't be used. - return; - } + // If the tenant is 'Uninitialized' there is no registered 'ISession' and then 'ISiteService' can't be used. + return; + } - var mediaTokenSettings = await _siteService.GetSettingsAsync(); + var mediaTokenSettings = await _siteService.GetSettingsAsync(); - if (mediaTokenSettings.HashKey == null) - { - var siteSettings = await _siteService.LoadSiteSettingsAsync(); + if (mediaTokenSettings.HashKey == null) + { + var siteSettings = await _siteService.LoadSiteSettingsAsync(); - mediaTokenSettings.HashKey = RandomNumberGenerator.GetBytes(DefaultMediaTokenKeySize); - siteSettings.Put(mediaTokenSettings); + mediaTokenSettings.HashKey = RandomNumberGenerator.GetBytes(DefaultMediaTokenKeySize); + siteSettings.Put(mediaTokenSettings); - await _siteService.UpdateSiteSettingsAsync(siteSettings); - } + await _siteService.UpdateSiteSettingsAsync(siteSettings); } + } - public Task ActivatingAsync() => Task.CompletedTask; - public Task RemovingAsync(ShellRemovingContext context) => Task.CompletedTask; - public Task TerminatedAsync() => Task.CompletedTask; - public Task TerminatingAsync() => Task.CompletedTask; + public Task ActivatingAsync() => Task.CompletedTask; + public Task RemovingAsync(ShellRemovingContext context) => Task.CompletedTask; + public Task TerminatedAsync() => Task.CompletedTask; + public Task TerminatingAsync() => Task.CompletedTask; - public override Task EnabledAsync(IFeatureInfo feature) => SetMediaTokenSettingsAsync(feature); + public override Task EnabledAsync(IFeatureInfo feature) => SetMediaTokenSettingsAsync(feature); - private async Task SetMediaTokenSettingsAsync(IFeatureInfo feature) + private async Task SetMediaTokenSettingsAsync(IFeatureInfo feature) + { + if (feature.Extension.Id != GetType().Assembly.GetName().Name) { - if (feature.Extension.Id != GetType().Assembly.GetName().Name) - { - return; - } + return; + } - var siteSettings = await _siteService.LoadSiteSettingsAsync(); - var mediaTokenSettings = siteSettings.As(); + var siteSettings = await _siteService.LoadSiteSettingsAsync(); + var mediaTokenSettings = siteSettings.As(); - mediaTokenSettings.HashKey = RandomNumberGenerator.GetBytes(DefaultMediaTokenKeySize); - siteSettings.Put(mediaTokenSettings); + mediaTokenSettings.HashKey = RandomNumberGenerator.GetBytes(DefaultMediaTokenKeySize); + siteSettings.Put(mediaTokenSettings); - await _siteService.UpdateSiteSettingsAsync(siteSettings); - } + await _siteService.UpdateSiteSettingsAsync(siteSettings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Processing/TokenCommandProcessor.cs b/src/OrchardCore.Modules/OrchardCore.Media/Processing/TokenCommandProcessor.cs index 62139dfa808..6ab57e72be6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Processing/TokenCommandProcessor.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Processing/TokenCommandProcessor.cs @@ -5,27 +5,26 @@ using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.Processors; -namespace OrchardCore.Media.Processing +namespace OrchardCore.Media.Processing; + +/// +/// Pass through processor which allows inclusion of a tokenized query string. +/// +public class TokenCommandProcessor : IImageWebProcessor { /// - /// Pass through processor which allows inclusion of a tokenized query string. + /// The command constant for a tokenized query string. /// - public class TokenCommandProcessor : IImageWebProcessor - { - /// - /// The command constant for a tokenized query string. - /// - public const string TokenCommand = "token"; + public const string TokenCommand = "token"; - private static readonly IEnumerable _tokenCommands = new[] { TokenCommand }; + private static readonly IEnumerable _tokenCommands = new[] { TokenCommand }; - public IEnumerable Commands - => _tokenCommands; + public IEnumerable Commands + => _tokenCommands; - public FormattedImage Process(FormattedImage image, ILogger logger, CommandCollection commands, CommandParser parser, CultureInfo culture) - => image; + public FormattedImage Process(FormattedImage image, ILogger logger, CommandCollection commands, CommandParser parser, CultureInfo culture) + => image; - public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) - => false; - } + public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) + => false; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaProfileStep.cs b/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaProfileStep.cs index 883d89e33ed..15ab1057f79 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaProfileStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaProfileStep.cs @@ -7,38 +7,37 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Media.Recipes +namespace OrchardCore.Media.Recipes; + +/// +/// This recipe step creates or updates a media profile. +/// +public sealed class MediaProfileStep : IRecipeStepHandler { - /// - /// This recipe step creates or updates a media profile. - /// - public sealed class MediaProfileStep : IRecipeStepHandler + private readonly MediaProfilesManager _mediaProfilesManager; + + public MediaProfileStep(MediaProfilesManager mediaProfilesManager) { - private readonly MediaProfilesManager _mediaProfilesManager; + _mediaProfilesManager = mediaProfilesManager; + } - public MediaProfileStep(MediaProfilesManager mediaProfilesManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "MediaProfiles", StringComparison.OrdinalIgnoreCase)) { - _mediaProfilesManager = mediaProfilesManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "MediaProfiles", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); + var model = context.Step.ToObject(); - foreach (var mediaProfile in model.MediaProfiles) - { - await _mediaProfilesManager.UpdateMediaProfileAsync(mediaProfile.Key, mediaProfile.Value); - } + foreach (var mediaProfile in model.MediaProfiles) + { + await _mediaProfilesManager.UpdateMediaProfileAsync(mediaProfile.Key, mediaProfile.Value); } } +} - public sealed class MediaProfileStepModel - { - public Dictionary MediaProfiles { get; set; } - } +public sealed class MediaProfileStepModel +{ + public Dictionary MediaProfiles { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaStep.cs b/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaStep.cs index 02b829b1d15..e80ce0addf1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Recipes/MediaStep.cs @@ -11,132 +11,131 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Media.Recipes +namespace OrchardCore.Media.Recipes; + +/// +/// This recipe step creates a set of queries. +/// +public sealed class MediaStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of queries. - /// - public sealed class MediaStep : IRecipeStepHandler - { - private readonly IMediaFileStore _mediaFileStore; - private readonly HashSet _allowedFileExtensions; - private readonly IHttpClientFactory _httpClientFactory; + private readonly IMediaFileStore _mediaFileStore; + private readonly HashSet _allowedFileExtensions; + private readonly IHttpClientFactory _httpClientFactory; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; + + public MediaStep( + IMediaFileStore mediaFileStore, + IOptions options, + IHttpClientFactory httpClientFactory, + IStringLocalizer stringLocalizer) + { + _mediaFileStore = mediaFileStore; + _allowedFileExtensions = options.Value.AllowedFileExtensions; + _httpClientFactory = httpClientFactory; + S = stringLocalizer; + } - public MediaStep( - IMediaFileStore mediaFileStore, - IOptions options, - IHttpClientFactory httpClientFactory, - IStringLocalizer stringLocalizer) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "media", StringComparison.OrdinalIgnoreCase)) { - _mediaFileStore = mediaFileStore; - _allowedFileExtensions = options.Value.AllowedFileExtensions; - _httpClientFactory = httpClientFactory; - S = stringLocalizer; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + var model = context.Step.ToObject(); + + foreach (var file in model.Files) { - if (!string.Equals(context.Name, "media", StringComparison.OrdinalIgnoreCase)) + if (!_allowedFileExtensions.Contains(Path.GetExtension(file.TargetPath), StringComparer.OrdinalIgnoreCase)) { - return; + context.Errors.Add(S["File extension not allowed: '{0}'", file.TargetPath]); + + continue; } - var model = context.Step.ToObject(); + Stream stream = null; - foreach (var file in model.Files) + if (!string.IsNullOrWhiteSpace(file.Base64)) { - if (!_allowedFileExtensions.Contains(Path.GetExtension(file.TargetPath), StringComparer.OrdinalIgnoreCase)) - { - context.Errors.Add(S["File extension not allowed: '{0}'", file.TargetPath]); + stream = new MemoryStream(Convert.FromBase64String(file.Base64)); + } + else if (!string.IsNullOrWhiteSpace(file.SourcePath)) + { + var fileInfo = context.RecipeDescriptor.FileProvider.GetRelativeFileInfo(context.RecipeDescriptor.BasePath, file.SourcePath); - continue; - } + stream = fileInfo.CreateReadStream(); + } + else if (!string.IsNullOrWhiteSpace(file.SourceUrl)) + { + var httpClient = _httpClientFactory.CreateClient(); - Stream stream = null; + var response = await httpClient.GetAsync(file.SourceUrl); - if (!string.IsNullOrWhiteSpace(file.Base64)) + if (response.IsSuccessStatusCode) { - stream = new MemoryStream(Convert.FromBase64String(file.Base64)); + stream = await response.Content.ReadAsStreamAsync(); } - else if (!string.IsNullOrWhiteSpace(file.SourcePath)) - { - var fileInfo = context.RecipeDescriptor.FileProvider.GetRelativeFileInfo(context.RecipeDescriptor.BasePath, file.SourcePath); + } - stream = fileInfo.CreateReadStream(); - } - else if (!string.IsNullOrWhiteSpace(file.SourceUrl)) + if (stream != null) + { + try { - var httpClient = _httpClientFactory.CreateClient(); - - var response = await httpClient.GetAsync(file.SourceUrl); - - if (response.IsSuccessStatusCode) - { - stream = await response.Content.ReadAsStreamAsync(); - } + await _mediaFileStore.CreateFileFromStreamAsync(file.TargetPath, stream, true); } - - if (stream != null) + finally { - try - { - await _mediaFileStore.CreateFileFromStreamAsync(file.TargetPath, stream, true); - } - finally - { - await stream.DisposeAsync(); - } + await stream.DisposeAsync(); } } } + } - private sealed class MediaStepModel - { - /// - /// Collection of objects. - /// - public MediaStepFile[] Files { get; set; } - } + private sealed class MediaStepModel + { + /// + /// Collection of objects. + /// + public MediaStepFile[] Files { get; set; } + } - private sealed class MediaStepFile - { - /// - /// Path where the content will be written. - /// Use inter-changeably with . - /// - public string Path { get => TargetPath; set => TargetPath = value; } - - /// - /// Path where the content will be written. - /// Use inter-changeably with . - /// - public string TargetPath { get; set; } - - /// - /// Base64 encoded content. Use when the source file will - /// not be available in this recipe step's file provider. - /// If both this and SourcePath properties are set with - /// non-null values, this property will be used. - /// - public string Base64 { get; set; } - - /// - /// Path where the content is read from. Use when the file - /// will be available in this recipe step's file provider. - /// If both this and Base64 properties are set with - /// non-null values, the Base64 property will be used. - /// - public string SourcePath { get; set; } - - /// - /// URL where the content is read from. Use when the file - /// will be available on a remote website. - /// If Base64 property or SourcePath property are set, they take - /// precedence over SourceUrl. - /// - public string SourceUrl { get; set; } - } + private sealed class MediaStepFile + { + /// + /// Path where the content will be written. + /// Use inter-changeably with . + /// + public string Path { get => TargetPath; set => TargetPath = value; } + + /// + /// Path where the content will be written. + /// Use inter-changeably with . + /// + public string TargetPath { get; set; } + + /// + /// Base64 encoded content. Use when the source file will + /// not be available in this recipe step's file provider. + /// If both this and SourcePath properties are set with + /// non-null values, this property will be used. + /// + public string Base64 { get; set; } + + /// + /// Path where the content is read from. Use when the file + /// will be available in this recipe step's file provider. + /// If both this and Base64 properties are set with + /// non-null values, the Base64 property will be used. + /// + public string SourcePath { get; set; } + + /// + /// URL where the content is read from. Use when the file + /// will be available on a remote website. + /// If Base64 property or SourcePath property are set, they take + /// precedence over SourceUrl. + /// + public string SourceUrl { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/MediaMetaWeblogDriver.cs b/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/MediaMetaWeblogDriver.cs index f82e242b984..cf4d8b17e2c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/MediaMetaWeblogDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/MediaMetaWeblogDriver.cs @@ -1,13 +1,12 @@ using System; using OrchardCore.MetaWeblog; -namespace OrchardCore.Media.RemotePublishing +namespace OrchardCore.Media.RemotePublishing; + +public sealed class MediaMetaWeblogDriver : MetaWeblogDriver { - public sealed class MediaMetaWeblogDriver : MetaWeblogDriver + public override void SetCapabilities(Action setCapability) { - public override void SetCapabilities(Action setCapability) - { - setCapability("supportsFileUpload", "Yes"); - } + setCapability("supportsFileUpload", "Yes"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/RemotePublishingStartup.cs b/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/RemotePublishingStartup.cs index 6cbc5cd5f1a..294fb55afc9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/RemotePublishingStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/RemotePublishing/RemotePublishingStartup.cs @@ -2,14 +2,13 @@ using OrchardCore.MetaWeblog; using OrchardCore.Modules; -namespace OrchardCore.Media.RemotePublishing +namespace OrchardCore.Media.RemotePublishing; + +[RequireFeatures("OrchardCore.RemotePublishing")] +public sealed class RemotePublishingStartup : StartupBase { - [RequireFeatures("OrchardCore.RemotePublishing")] - public sealed class RemotePublishingStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ResourceManifestOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Media/ResourceManifestOptionsConfiguration.cs index 50e933d1be7..f47af586ea3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ResourceManifestOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ResourceManifestOptionsConfiguration.cs @@ -1,32 +1,31 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.Media +namespace OrchardCore.Media; + +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions - { - private static readonly ResourceManifest _manifest; + private static readonly ResourceManifest _manifest; - static ResourceManagementOptionsConfiguration() - { - _manifest = new ResourceManifest(); + static ResourceManagementOptionsConfiguration() + { + _manifest = new ResourceManifest(); - _manifest - .DefineScript("media") - .SetUrl("~/OrchardCore.Media/Scripts/media.min.js", "~/OrchardCore.Media/Scripts/media.js") - .SetDependencies("vuejs", "Sortable", "vuedraggable", "jQuery-ui", "credential-helpers") - .SetVersion("1.0.0"); + _manifest + .DefineScript("media") + .SetUrl("~/OrchardCore.Media/Scripts/media.min.js", "~/OrchardCore.Media/Scripts/media.js") + .SetDependencies("vuejs", "Sortable", "vuedraggable", "jQuery-ui", "credential-helpers") + .SetVersion("1.0.0"); - _manifest - .DefineStyle("media") - .SetUrl("~/OrchardCore.Media/Styles/media.min.css", "~/OrchardCore.Media/Styles/media.css") - .SetVersion("1.0.0"); + _manifest + .DefineStyle("media") + .SetUrl("~/OrchardCore.Media/Styles/media.min.css", "~/OrchardCore.Media/Styles/media.css") + .SetVersion("1.0.0"); - } + } - public void Configure(ResourceManagementOptions options) - { - options.ResourceManifests.Add(_manifest); - } + public void Configure(ResourceManagementOptions options) + { + options.ResourceManifests.Add(_manifest); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/SecureMediaPermissions.cs b/src/OrchardCore.Modules/OrchardCore.Media/SecureMediaPermissions.cs index 1ad9a0a02a4..1e3694318df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/SecureMediaPermissions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/SecureMediaPermissions.cs @@ -9,157 +9,156 @@ using OrchardCore.Media.Services; using OrchardCore.Security.Permissions; -namespace OrchardCore.Media +namespace OrchardCore.Media; + +public sealed class SecureMediaPermissions : IPermissionProvider { - public sealed class SecureMediaPermissions : IPermissionProvider - { - // Note: The ManageMediaFolder permission grants all access, so viewing must be implied by it too. - public static readonly Permission ViewMedia = new("ViewMediaContent", "View media content in all folders", new[] { Permissions.ManageMediaFolder }); - public static readonly Permission ViewRootMedia = new("ViewRootMediaContent", "View media content in the root folder", new[] { ViewMedia }); - public static readonly Permission ViewOthersMedia = new("ViewOthersMediaContent", "View others media content", new[] { Permissions.ManageMediaFolder }); - public static readonly Permission ViewOwnMedia = new("ViewOwnMediaContent", "View own media content", new[] { ViewOthersMedia }); + // Note: The ManageMediaFolder permission grants all access, so viewing must be implied by it too. + public static readonly Permission ViewMedia = new("ViewMediaContent", "View media content in all folders", new[] { Permissions.ManageMediaFolder }); + public static readonly Permission ViewRootMedia = new("ViewRootMediaContent", "View media content in the root folder", new[] { ViewMedia }); + public static readonly Permission ViewOthersMedia = new("ViewOthersMediaContent", "View others media content", new[] { Permissions.ManageMediaFolder }); + public static readonly Permission ViewOwnMedia = new("ViewOwnMediaContent", "View own media content", new[] { ViewOthersMedia }); - private static readonly Permission _viewMediaTemplate = new("ViewMediaContent_{0}", "View media content in folder '{0}'", new[] { ViewMedia }); + private static readonly Permission _viewMediaTemplate = new("ViewMediaContent_{0}", "View media content in folder '{0}'", new[] { ViewMedia }); - private static Dictionary, Permission> _permissionsByFolder = new(); - private static readonly char[] _trimSecurePathChars = ['/', '\\', ' ']; - private static readonly ReadOnlyDictionary _permissionTemplates = new(new Dictionary() - { - { ViewMedia.Name, _viewMediaTemplate }, - }); + private static Dictionary, Permission> _permissionsByFolder = new(); + private static readonly char[] _trimSecurePathChars = ['/', '\\', ' ']; + private static readonly ReadOnlyDictionary _permissionTemplates = new(new Dictionary() + { + { ViewMedia.Name, _viewMediaTemplate }, + }); + + private readonly MediaOptions _mediaOptions; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + private readonly ISignal _signal; + private readonly IMediaFileStore _fileStore; + private readonly IMemoryCache _cache; + + public SecureMediaPermissions( + IOptions options, + IMediaFileStore fileStore, + IMemoryCache cache, + AttachedMediaFieldFileService attachedMediaFieldFileService, + ISignal signal) + { + _mediaOptions = options.Value; + _fileStore = fileStore; + _cache = cache; + _attachedMediaFieldFileService = attachedMediaFieldFileService; + _signal = signal; + } - private readonly MediaOptions _mediaOptions; - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - private readonly ISignal _signal; - private readonly IMediaFileStore _fileStore; - private readonly IMemoryCache _cache; - - public SecureMediaPermissions( - IOptions options, - IMediaFileStore fileStore, - IMemoryCache cache, - AttachedMediaFieldFileService attachedMediaFieldFileService, - ISignal signal) + public async Task> GetPermissionsAsync() + { + return await _cache.GetOrCreateAsync(nameof(SecureMediaPermissions), async (entry) => { - _mediaOptions = options.Value; - _fileStore = fileStore; - _cache = cache; - _attachedMediaFieldFileService = attachedMediaFieldFileService; - _signal = signal; - } + // Ensure to rebuild at least after some time, to detect directory changes from outside of + // the media module. The signal gets set if a directory is created or deleted in the Media + // Library directly. + entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)) + .AddExpirationToken(_signal.GetToken(nameof(SecureMediaPermissions))); - public async Task> GetPermissionsAsync() - { - return await _cache.GetOrCreateAsync(nameof(SecureMediaPermissions), async (entry) => - { - // Ensure to rebuild at least after some time, to detect directory changes from outside of - // the media module. The signal gets set if a directory is created or deleted in the Media - // Library directly. - entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5)) - .AddExpirationToken(_signal.GetToken(nameof(SecureMediaPermissions))); - - return await GetPermissionsInternalAsync(); - }); - } + return await GetPermissionsInternalAsync(); + }); + } - public IEnumerable GetDefaultStereotypes() + public IEnumerable GetDefaultStereotypes() + { + return new[] { - return new[] + new PermissionStereotype { - new PermissionStereotype + Name = "Administrator", + Permissions = new[] { - Name = "Administrator", - Permissions = new[] - { - ViewMedia, - ViewOthersMedia - } - }, - new PermissionStereotype + ViewMedia, + ViewOthersMedia + } + }, + new PermissionStereotype + { + Name = "Authenticated", + Permissions = new[] { - Name = "Authenticated", - Permissions = new[] - { - ViewOwnMedia - } - }, - new PermissionStereotype + ViewOwnMedia + } + }, + new PermissionStereotype + { + Name = "Anonymous", + Permissions = new[] { - Name = "Anonymous", - Permissions = new[] - { - ViewMedia - } + ViewMedia } - }; - } + } + }; + } - /// - /// Returns a dynamic permission for a secure folder, based on a global view media permission template. - /// - internal static Permission ConvertToDynamicPermission(Permission permission) => _permissionTemplates.TryGetValue(permission.Name, out var result) ? result : null; + /// + /// Returns a dynamic permission for a secure folder, based on a global view media permission template. + /// + internal static Permission ConvertToDynamicPermission(Permission permission) => _permissionTemplates.TryGetValue(permission.Name, out var result) ? result : null; - internal static Permission CreateDynamicPermission(Permission template, string secureFolder) - { - ArgumentNullException.ThrowIfNull(template); + internal static Permission CreateDynamicPermission(Permission template, string secureFolder) + { + ArgumentNullException.ThrowIfNull(template); - secureFolder = secureFolder?.Trim(_trimSecurePathChars); + secureFolder = secureFolder?.Trim(_trimSecurePathChars); - var key = new ValueTuple(template.Name, secureFolder); + var key = new ValueTuple(template.Name, secureFolder); - if (_permissionsByFolder.TryGetValue(key, out var permission)) - { - return permission; - } + if (_permissionsByFolder.TryGetValue(key, out var permission)) + { + return permission; + } - permission = new Permission( - string.Format(template.Name, secureFolder), - string.Format(template.Description, secureFolder), - (template.ImpliedBy ?? Array.Empty()).Select(t => CreateDynamicPermission(t, secureFolder)) - ); + permission = new Permission( + string.Format(template.Name, secureFolder), + string.Format(template.Description, secureFolder), + (template.ImpliedBy ?? Array.Empty()).Select(t => CreateDynamicPermission(t, secureFolder)) + ); - var localPermissions = new Dictionary, Permission>(_permissionsByFolder) - { - [key] = permission, - }; + var localPermissions = new Dictionary, Permission>(_permissionsByFolder) + { + [key] = permission, + }; - _permissionsByFolder = localPermissions; + _permissionsByFolder = localPermissions; - return permission; - } + return permission; + } - private async Task> GetPermissionsInternalAsync() + private async Task> GetPermissionsInternalAsync() + { + // The ViewRootMedia permission must be implied by any subfolder permission. + var viewRootImpliedBy = new List(ViewRootMedia.ImpliedBy); + var result = new List() { - // The ViewRootMedia permission must be implied by any subfolder permission. - var viewRootImpliedBy = new List(ViewRootMedia.ImpliedBy); - var result = new List() - { - ViewMedia, - new (ViewRootMedia.Name, ViewRootMedia.Description, viewRootImpliedBy), - ViewOthersMedia, - ViewOwnMedia - }; + ViewMedia, + new (ViewRootMedia.Name, ViewRootMedia.Description, viewRootImpliedBy), + ViewOthersMedia, + ViewOwnMedia + }; - await foreach (var entry in _fileStore.GetDirectoryContentAsync()) - { - if (!entry.IsDirectory) - continue; + await foreach (var entry in _fileStore.GetDirectoryContentAsync()) + { + if (!entry.IsDirectory) + continue; - if (entry.Name == _mediaOptions.AssetsUsersFolder || - entry.Name == _attachedMediaFieldFileService.MediaFieldsFolder) - continue; + if (entry.Name == _mediaOptions.AssetsUsersFolder || + entry.Name == _attachedMediaFieldFileService.MediaFieldsFolder) + continue; - var folderPath = entry.Path; + var folderPath = entry.Path; - foreach (var template in _permissionTemplates) - { - var dynamicPermission = CreateDynamicPermission(template.Value, folderPath); - result.Add(dynamicPermission); - viewRootImpliedBy.Add(dynamicPermission); - } + foreach (var template in _permissionTemplates) + { + var dynamicPermission = CreateDynamicPermission(template.Value, folderPath); + result.Add(dynamicPermission); + viewRootImpliedBy.Add(dynamicPermission); } - - return result.AsEnumerable(); } + + return result.AsEnumerable(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/AttachedMediaFieldFileService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/AttachedMediaFieldFileService.cs index 43e63d89616..54bcf51e5ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/AttachedMediaFieldFileService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/AttachedMediaFieldFileService.cs @@ -10,153 +10,152 @@ using OrchardCore.FileStorage; using OrchardCore.Media.ViewModels; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +/// +/// Handles file management operations related to the attached media field. +/// +public class AttachedMediaFieldFileService { + private readonly IMediaFileStore _fileStore; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; + private readonly ILogger _logger; + + public AttachedMediaFieldFileService( + IMediaFileStore fileStore, + IHttpContextAccessor httpContextAccessor, + IUserAssetFolderNameProvider userAssetFolderNameProvider, + ILogger logger) + { + _fileStore = fileStore; + _httpContextAccessor = httpContextAccessor; + _userAssetFolderNameProvider = userAssetFolderNameProvider; + _logger = logger; + + MediaFieldsFolder = "mediafields"; + MediaFieldsTempSubFolder = _fileStore.Combine(MediaFieldsFolder, "temp"); + } + + public string MediaFieldsFolder { get; } + public string MediaFieldsTempSubFolder { get; } + /// - /// Handles file management operations related to the attached media field. + /// Removes the assets attached to a content item through an attached media field. /// - public class AttachedMediaFieldFileService + /// + /// To be used by content handlers when the content item is deleted. + /// + public Task DeleteContentItemFolderAsync(ContentItem contentItem) { - private readonly IMediaFileStore _fileStore; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; - private readonly ILogger _logger; - - public AttachedMediaFieldFileService( - IMediaFileStore fileStore, - IHttpContextAccessor httpContextAccessor, - IUserAssetFolderNameProvider userAssetFolderNameProvider, - ILogger logger) - { - _fileStore = fileStore; - _httpContextAccessor = httpContextAccessor; - _userAssetFolderNameProvider = userAssetFolderNameProvider; - _logger = logger; - - MediaFieldsFolder = "mediafields"; - MediaFieldsTempSubFolder = _fileStore.Combine(MediaFieldsFolder, "temp"); - } + return _fileStore.TryDeleteDirectoryAsync(GetContentItemFolder(contentItem)); + } - public string MediaFieldsFolder { get; } - public string MediaFieldsTempSubFolder { get; } + /// + /// Moves uploaded files to a folder specific for the content item. + /// + public async Task HandleFilesOnFieldUpdateAsync(List items, ContentItem contentItem) + { + ArgumentNullException.ThrowIfNull(items); - /// - /// Removes the assets attached to a content item through an attached media field. - /// - /// - /// To be used by content handlers when the content item is deleted. - /// - public Task DeleteContentItemFolderAsync(ContentItem contentItem) + if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) { - return _fileStore.TryDeleteDirectoryAsync(GetContentItemFolder(contentItem)); + return; } - /// - /// Moves uploaded files to a folder specific for the content item. - /// - public async Task HandleFilesOnFieldUpdateAsync(List items, ContentItem contentItem) - { - ArgumentNullException.ThrowIfNull(items); - - if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) - { - return; - } + await EnsureGlobalDirectoriesAsync(); - await EnsureGlobalDirectoriesAsync(); + await RemoveTemporaryAsync(items); - await RemoveTemporaryAsync(items); + await MoveNewFilesToContentItemDirAndUpdatePathsAsync(items, contentItem); + } - await MoveNewFilesToContentItemDirAndUpdatePathsAsync(items, contentItem); - } + /// + /// Gets the per-user temporary upload directory. + /// + /// + public string GetMediaFieldsTempSubFolder() + => _fileStore.Combine(MediaFieldsTempSubFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(_httpContextAccessor.HttpContext.User)); - /// - /// Gets the per-user temporary upload directory. - /// - /// - public string GetMediaFieldsTempSubFolder() - => _fileStore.Combine(MediaFieldsTempSubFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(_httpContextAccessor.HttpContext.User)); + private async Task EnsureGlobalDirectoriesAsync() + { + await _fileStore.TryCreateDirectoryAsync(MediaFieldsFolder); + await _fileStore.TryCreateDirectoryAsync(MediaFieldsTempSubFolder); + } - private async Task EnsureGlobalDirectoriesAsync() + // Files just uploaded and then immediately discarded. + private async Task RemoveTemporaryAsync(List items) + { + foreach (var item in items.Where(i => i.IsRemoved && i.IsNew)) { - await _fileStore.TryCreateDirectoryAsync(MediaFieldsFolder); - await _fileStore.TryCreateDirectoryAsync(MediaFieldsTempSubFolder); + await _fileStore.TryDeleteFileAsync(item.Path); } + } - // Files just uploaded and then immediately discarded. - private async Task RemoveTemporaryAsync(List items) + // Newly added files + private async Task MoveNewFilesToContentItemDirAndUpdatePathsAsync(List items, ContentItem contentItem) + { + foreach (var item in items.Where(i => !i.IsRemoved && !string.IsNullOrEmpty(i.Path))) { - foreach (var item in items.Where(i => i.IsRemoved && i.IsNew)) - { - await _fileStore.TryDeleteFileAsync(item.Path); - } - } + var fileInfo = await _fileStore.GetFileInfoAsync(item.Path); - // Newly added files - private async Task MoveNewFilesToContentItemDirAndUpdatePathsAsync(List items, ContentItem contentItem) - { - foreach (var item in items.Where(i => !i.IsRemoved && !string.IsNullOrEmpty(i.Path))) + if (fileInfo == null) { - var fileInfo = await _fileStore.GetFileInfoAsync(item.Path); - - if (fileInfo == null) - { - _logger.LogError("A file with the path '{Path}' does not exist.", item.Path); - return; - } + _logger.LogError("A file with the path '{Path}' does not exist.", item.Path); + return; + } - var targetDir = GetContentItemFolder(contentItem); - var finalFileName = (await GetFileHashAsync(item.Path)) + GetFileExtension(item.Path); - var finalFilePath = _fileStore.Combine(targetDir, finalFileName); + var targetDir = GetContentItemFolder(contentItem); + var finalFileName = (await GetFileHashAsync(item.Path)) + GetFileExtension(item.Path); + var finalFilePath = _fileStore.Combine(targetDir, finalFileName); - await _fileStore.TryCreateDirectoryAsync(targetDir); + await _fileStore.TryCreateDirectoryAsync(targetDir); - // When there is a validation error before creating the content item we can end up with an empty folder - // because the content item is different on each form submit . We need to remove that empty folder. - var previousDirPath = fileInfo.DirectoryPath; + // When there is a validation error before creating the content item we can end up with an empty folder + // because the content item is different on each form submit . We need to remove that empty folder. + var previousDirPath = fileInfo.DirectoryPath; - // fileName is a hash of the file. We preserve disk space by reusing the file. - if (await _fileStore.GetFileInfoAsync(finalFilePath) == null) - { - await _fileStore.MoveFileAsync(item.Path, finalFilePath); - } + // fileName is a hash of the file. We preserve disk space by reusing the file. + if (await _fileStore.GetFileInfoAsync(finalFilePath) == null) + { + await _fileStore.MoveFileAsync(item.Path, finalFilePath); + } - item.Path = finalFilePath; + item.Path = finalFilePath; - await DeleteDirIfEmptyAsync(previousDirPath); - } + await DeleteDirIfEmptyAsync(previousDirPath); } + } - private string GetContentItemFolder(ContentItem contentItem) - { - return _fileStore.Combine(MediaFieldsFolder, contentItem.ContentType, contentItem.ContentItemId); - } + private string GetContentItemFolder(ContentItem contentItem) + { + return _fileStore.Combine(MediaFieldsFolder, contentItem.ContentType, contentItem.ContentItemId); + } - private async Task GetFileHashAsync(string filePath) - { - using var fs = await _fileStore.GetFileStreamAsync(filePath); - var hash = new XxHash32(); - await hash.AppendAsync(fs); - return Convert.ToHexString(hash.GetCurrentHash()).ToLowerInvariant(); - } + private async Task GetFileHashAsync(string filePath) + { + using var fs = await _fileStore.GetFileStreamAsync(filePath); + var hash = new XxHash32(); + await hash.AppendAsync(fs); + return Convert.ToHexString(hash.GetCurrentHash()).ToLowerInvariant(); + } + + private static string GetFileExtension(string path) + { + var lastPoint = path.LastIndexOf('.'); + return lastPoint > -1 ? path[lastPoint..] : ""; + } - private static string GetFileExtension(string path) + private async Task DeleteDirIfEmptyAsync(string previousDirPath) + { + if (await _fileStore.GetDirectoryInfoAsync(previousDirPath) == null) { - var lastPoint = path.LastIndexOf('.'); - return lastPoint > -1 ? path[lastPoint..] : ""; + return; } - private async Task DeleteDirIfEmptyAsync(string previousDirPath) + if (!(await _fileStore.GetDirectoryContentAsync(previousDirPath).AnyAsync())) { - if (await _fileStore.GetDirectoryInfoAsync(previousDirPath) == null) - { - return; - } - - if (!(await _fileStore.GetDirectoryContentAsync(previousDirPath).AnyAsync())) - { - await _fileStore.TryDeleteDirectoryAsync(previousDirPath); - } + await _fileStore.TryDeleteDirectoryAsync(previousDirPath); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/DefaultUserAssetFolderNameProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/DefaultUserAssetFolderNameProvider.cs index 41343635ec7..fc606deb90b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/DefaultUserAssetFolderNameProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/DefaultUserAssetFolderNameProvider.cs @@ -1,12 +1,11 @@ using System.Security.Claims; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class DefaultUserAssetFolderNameProvider : IUserAssetFolderNameProvider { - public class DefaultUserAssetFolderNameProvider : IUserAssetFolderNameProvider + public string GetUserAssetFolderName(ClaimsPrincipal claimsPrincipal) { - public string GetUserAssetFolderName(ClaimsPrincipal claimsPrincipal) - { - return claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier); - } + return claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/IMediaProfileService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/IMediaProfileService.cs index 55f5dbe446d..92f9aee8fe0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/IMediaProfileService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/IMediaProfileService.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public interface IMediaProfileService { - public interface IMediaProfileService - { - Task> GetMediaProfileCommands(string name); - } + Task> GetMediaProfileCommands(string name); } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/ManageMediaFolderAuthorizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/ManageMediaFolderAuthorizationHandler.cs index 55c936bc49c..b160dc34dc0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/ManageMediaFolderAuthorizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/ManageMediaFolderAuthorizationHandler.cs @@ -6,113 +6,112 @@ using OrchardCore.FileStorage; using OrchardCore.Security; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +/// +/// Checks if the user has related permission to manage the path resource which is passed from AuthorizationHandler. +/// +public class ManageMediaFolderAuthorizationHandler : AuthorizationHandler { - /// - /// Checks if the user has related permission to manage the path resource which is passed from AuthorizationHandler. - /// - public class ManageMediaFolderAuthorizationHandler : AuthorizationHandler + private readonly IServiceProvider _serviceProvider; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + private readonly IMediaFileStore _fileStore; + private char _pathSeparator; + private string _mediaFieldsFolder; + private string _usersFolder; + private readonly MediaOptions _mediaOptions; + private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; + + public ManageMediaFolderAuthorizationHandler(IServiceProvider serviceProvider, + AttachedMediaFieldFileService attachedMediaFieldFileService, + IMediaFileStore fileStore, + IOptions options, + IUserAssetFolderNameProvider userAssetFolderNameProvider) { - private readonly IServiceProvider _serviceProvider; - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - private readonly IMediaFileStore _fileStore; - private char _pathSeparator; - private string _mediaFieldsFolder; - private string _usersFolder; - private readonly MediaOptions _mediaOptions; - private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; - - public ManageMediaFolderAuthorizationHandler(IServiceProvider serviceProvider, - AttachedMediaFieldFileService attachedMediaFieldFileService, - IMediaFileStore fileStore, - IOptions options, - IUserAssetFolderNameProvider userAssetFolderNameProvider) + _serviceProvider = serviceProvider; + _attachedMediaFieldFileService = attachedMediaFieldFileService; + _fileStore = fileStore; + _mediaOptions = options.Value; + _userAssetFolderNameProvider = userAssetFolderNameProvider; + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _serviceProvider = serviceProvider; - _attachedMediaFieldFileService = attachedMediaFieldFileService; - _fileStore = fileStore; - _mediaOptions = options.Value; - _userAssetFolderNameProvider = userAssetFolderNameProvider; + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + if (requirement.Permission.Name != Permissions.ManageMediaFolder.Name) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } - - if (requirement.Permission.Name != Permissions.ManageMediaFolder.Name) - { - return; - } + return; + } - if (context.Resource == null) - { - return; - } + if (context.Resource == null) + { + return; + } - _pathSeparator = _fileStore.Combine("a", "b").Contains('/') ? '/' : '\\'; + _pathSeparator = _fileStore.Combine("a", "b").Contains('/') ? '/' : '\\'; - // ensure end trailing slash - _mediaFieldsFolder = _fileStore.NormalizePath(_attachedMediaFieldFileService.MediaFieldsFolder) - .TrimEnd(_pathSeparator) + _pathSeparator; + // ensure end trailing slash + _mediaFieldsFolder = _fileStore.NormalizePath(_attachedMediaFieldFileService.MediaFieldsFolder) + .TrimEnd(_pathSeparator) + _pathSeparator; - _usersFolder = _fileStore.NormalizePath(_mediaOptions.AssetsUsersFolder) - .TrimEnd(_pathSeparator) + _pathSeparator; + _usersFolder = _fileStore.NormalizePath(_mediaOptions.AssetsUsersFolder) + .TrimEnd(_pathSeparator) + _pathSeparator; - var path = context.Resource as string; + var path = context.Resource as string; - var userOwnFolder = _fileStore.NormalizePath( - _fileStore.Combine(_usersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(context.User))) - .TrimEnd(_pathSeparator) + _pathSeparator; + var userOwnFolder = _fileStore.NormalizePath( + _fileStore.Combine(_usersFolder, _userAssetFolderNameProvider.GetUserAssetFolderName(context.User))) + .TrimEnd(_pathSeparator) + _pathSeparator; - var permission = Permissions.ManageMedia; + var permission = Permissions.ManageMedia; - // Handle attached media field folder. - if (IsAuthorizedFolder(_mediaFieldsFolder, path) || IsDescendantOfauthorizedFolder(_mediaFieldsFolder, path)) - { - permission = Permissions.ManageAttachedMediaFieldsFolder; - } + // Handle attached media field folder. + if (IsAuthorizedFolder(_mediaFieldsFolder, path) || IsDescendantOfauthorizedFolder(_mediaFieldsFolder, path)) + { + permission = Permissions.ManageAttachedMediaFieldsFolder; + } - if (IsAuthorizedFolder(_usersFolder, path) || IsAuthorizedFolder(userOwnFolder, path) || IsDescendantOfauthorizedFolder(userOwnFolder, path)) - { - permission = Permissions.ManageOwnMedia; - } + if (IsAuthorizedFolder(_usersFolder, path) || IsAuthorizedFolder(userOwnFolder, path) || IsDescendantOfauthorizedFolder(userOwnFolder, path)) + { + permission = Permissions.ManageOwnMedia; + } - if (IsDescendantOfauthorizedFolder(_usersFolder, path) && !IsAuthorizedFolder(userOwnFolder, path) && !IsDescendantOfauthorizedFolder(userOwnFolder, path)) - { - permission = Permissions.ManageOthersMedia; - } + if (IsDescendantOfauthorizedFolder(_usersFolder, path) && !IsAuthorizedFolder(userOwnFolder, path) && !IsDescendantOfauthorizedFolder(userOwnFolder, path)) + { + permission = Permissions.ManageOthersMedia; + } - // Lazy load to prevent circular dependencies. - var authorizationService = _serviceProvider.GetService(); + // Lazy load to prevent circular dependencies. + var authorizationService = _serviceProvider.GetService(); - if (await authorizationService.AuthorizeAsync(context.User, permission)) + if (await authorizationService.AuthorizeAsync(context.User, permission)) + { + // Check if viewing is allowed for this folder, if secure media is also enabled. + if (!_serviceProvider.IsSecureMediaEnabled() || + await authorizationService.AuthorizeAsync(context.User, SecureMediaPermissions.ViewMedia, (object)path)) { - // Check if viewing is allowed for this folder, if secure media is also enabled. - if (!_serviceProvider.IsSecureMediaEnabled() || - await authorizationService.AuthorizeAsync(context.User, SecureMediaPermissions.ViewMedia, (object)path)) - { - context.Succeed(requirement); - } + context.Succeed(requirement); } } + } - private bool IsAuthorizedFolder(string authorizedFolder, string childPath) - { - // Ensure end trailing slash. - childPath = _fileStore.NormalizePath(childPath) - .TrimEnd(_pathSeparator) + _pathSeparator; + private bool IsAuthorizedFolder(string authorizedFolder, string childPath) + { + // Ensure end trailing slash. + childPath = _fileStore.NormalizePath(childPath) + .TrimEnd(_pathSeparator) + _pathSeparator; - return childPath.Equals(authorizedFolder, StringComparison.Ordinal); - } + return childPath.Equals(authorizedFolder, StringComparison.Ordinal); + } - private bool IsDescendantOfauthorizedFolder(string authorizedFolder, string childPath) - { - childPath = _fileStore.NormalizePath(childPath); - return childPath.StartsWith(authorizedFolder, StringComparison.Ordinal); - } + private bool IsDescendantOfauthorizedFolder(string authorizedFolder, string childPath) + { + childPath = _fileStore.NormalizePath(childPath); + return childPath.StartsWith(authorizedFolder, StringComparison.Ordinal); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileProvider.cs index e21a7847d7a..02f858190b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileProvider.cs @@ -2,20 +2,19 @@ using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders.Physical; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class MediaFileProvider : PhysicalFileProvider, IMediaFileProvider { - public class MediaFileProvider : PhysicalFileProvider, IMediaFileProvider + public MediaFileProvider(PathString virtualPathBase, string root) : base(root) { - public MediaFileProvider(PathString virtualPathBase, string root) : base(root) - { - VirtualPathBase = virtualPathBase; - } - - public MediaFileProvider(PathString virtualPathBase, string root, ExclusionFilters filters) : base(root, filters) - { - VirtualPathBase = virtualPathBase; - } + VirtualPathBase = virtualPathBase; + } - public PathString VirtualPathBase { get; } + public MediaFileProvider(PathString virtualPathBase, string root, ExclusionFilters filters) : base(root, filters) + { + VirtualPathBase = virtualPathBase; } + + public PathString VirtualPathBase { get; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileStoreResolverMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileStoreResolverMiddleware.cs index 2df251d0655..5f99402e0dc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileStoreResolverMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaFileStoreResolverMiddleware.cs @@ -7,106 +7,105 @@ using Microsoft.Extensions.Options; using OrchardCore.Routing; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +/// +/// Provides local caching for remote file store files. +/// +public class MediaFileStoreResolverMiddleware { - /// - /// Provides local caching for remote file store files. - /// - public class MediaFileStoreResolverMiddleware - { - private readonly ConcurrentDictionary> _workers = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary> _workers = new(StringComparer.OrdinalIgnoreCase); - private readonly RequestDelegate _next; - private readonly ILogger _logger; - private readonly IMediaFileStoreCache _mediaFileStoreCache; - private readonly IMediaFileStore _mediaFileStore; + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly IMediaFileStoreCache _mediaFileStoreCache; + private readonly IMediaFileStore _mediaFileStore; - private readonly PathString _assetsRequestPath; + private readonly PathString _assetsRequestPath; - public MediaFileStoreResolverMiddleware( - RequestDelegate next, - ILogger logger, - IMediaFileStoreCache mediaFileStoreCache, - IMediaFileStore mediaFileStore, - IOptions mediaOptions - ) - { - _next = next; - _logger = logger; - _mediaFileStoreCache = mediaFileStoreCache; - _mediaFileStore = mediaFileStore; + public MediaFileStoreResolverMiddleware( + RequestDelegate next, + ILogger logger, + IMediaFileStoreCache mediaFileStoreCache, + IMediaFileStore mediaFileStore, + IOptions mediaOptions + ) + { + _next = next; + _logger = logger; + _mediaFileStoreCache = mediaFileStoreCache; + _mediaFileStore = mediaFileStore; - _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; + _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; + } + + /// + /// Processes a request to determine if it matches a known file from the media file store, and cache it locally. + /// + /// + public async Task Invoke(HttpContext context) + { + // Support only Head requests or Get Requests. + if (!HttpMethods.IsGet(context.Request.Method) && !HttpMethods.IsHead(context.Request.Method)) + { + await _next(context); + return; } - /// - /// Processes a request to determine if it matches a known file from the media file store, and cache it locally. - /// - /// - public async Task Invoke(HttpContext context) + var validatePath = context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase, out var subPath); + if (!validatePath) { - // Support only Head requests or Get Requests. - if (!HttpMethods.IsGet(context.Request.Method) && !HttpMethods.IsHead(context.Request.Method)) - { - await _next(context); - return; - } + _logger.LogDebug("Request path {Path} does not match the assets request path {RequestPath}", subPath, _assetsRequestPath); + await _next(context); + return; + } - var validatePath = context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase, out var subPath); - if (!validatePath) + // subPath.Value returns an unescaped path value, subPath returns an escaped path value. + var subPathValue = subPath.Value; + + var isFileCached = await _mediaFileStoreCache.IsCachedAsync(subPathValue); + if (isFileCached) + { + // When multiple requests occur for the same file the download + // may already be in progress so we wait for it to complete. + if (_workers.TryGetValue(subPathValue, out var writeTask)) { - _logger.LogDebug("Request path {Path} does not match the assets request path {RequestPath}", subPath, _assetsRequestPath); - await _next(context); - return; + await writeTask.Value; } - // subPath.Value returns an unescaped path value, subPath returns an escaped path value. - var subPathValue = subPath.Value; + await _next(context); + return; + } - var isFileCached = await _mediaFileStoreCache.IsCachedAsync(subPathValue); - if (isFileCached) + // When multiple requests occur for the same file we use a Lazy + // to initialize the file store request once. + await _workers.GetOrAdd(subPathValue, path => new Lazy(async () => + { + try { - // When multiple requests occur for the same file the download - // may already be in progress so we wait for it to complete. - if (_workers.TryGetValue(subPathValue, out var writeTask)) + var fileStoreEntry = await _mediaFileStore.GetFileInfoAsync(path); + + if (fileStoreEntry != null) { - await writeTask.Value; + using var stream = await _mediaFileStore.GetFileStreamAsync(fileStoreEntry); + await _mediaFileStoreCache.SetCacheAsync(stream, fileStoreEntry, context.RequestAborted); } - - await _next(context); - return; } - - // When multiple requests occur for the same file we use a Lazy - // to initialize the file store request once. - await _workers.GetOrAdd(subPathValue, path => new Lazy(async () => + catch (Exception ex) { - try - { - var fileStoreEntry = await _mediaFileStore.GetFileInfoAsync(path); - - if (fileStoreEntry != null) - { - using var stream = await _mediaFileStore.GetFileStreamAsync(fileStoreEntry); - await _mediaFileStoreCache.SetCacheAsync(stream, fileStoreEntry, context.RequestAborted); - } - } - catch (Exception ex) - { - // Log the error, and pass to pipeline to handle as 404. - // Multiple requests at the same time will all receive the same 404 - // as we use LazyThreadSafetyMode.ExecutionAndPublication. - _logger.LogError(ex, "Error retrieving file from media file store for request path {Path}", path); - } - finally - { - _workers.TryRemove(path, out var writeTask); - } - }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; + // Log the error, and pass to pipeline to handle as 404. + // Multiple requests at the same time will all receive the same 404 + // as we use LazyThreadSafetyMode.ExecutionAndPublication. + _logger.LogError(ex, "Error retrieving file from media file store for request path {Path}", path); + } + finally + { + _workers.TryRemove(path, out var writeTask); + } + }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; - // Always call next, this middleware always passes. - await _next(context); - return; - } + // Always call next, this middleware always passes. + await _next(context); + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaOptionsConfiguration.cs index 61eabb75f0f..df03c27e300 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaOptionsConfiguration.cs @@ -6,124 +6,123 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell.Configuration; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public sealed class MediaOptionsConfiguration : IConfigureOptions { - public sealed class MediaOptionsConfiguration : IConfigureOptions + private static readonly int[] _defaultSupportedSizes = [16, 32, 50, 100, 160, 240, 480, 600, 1024, 2048]; + + private static readonly string[] _defaultAllowedFileExtensions = [ + // Images + ".jpg", + ".jpeg", + ".png", + ".gif", + ".ico", + ".svg", + ".webp", + + // Documents + ".pdf", // (Portable Document Format; Adobe Acrobat) + ".doc", + ".docx", // (Microsoft Word Document) + ".ppt", + ".pptx", + ".pps", + ".ppsx", // (Microsoft PowerPoint Presentation) + ".odt", // (OpenDocument Text Document) + ".xls", + ".xlsx", // (Microsoft Excel Document) + ".psd", // (Adobe Photoshop Document) + + // Audio + ".mp3", + ".m4a", + ".ogg", + ".wav", + + // Video + ".mp4", + ".m4v", // (MPEG-4) + ".mov", // (QuickTime) + ".wmv", // (Windows Media Video) + ".avi", + ".mpg", + ".ogv", // (Ogg) + ".3gp", // (3GPP) + ]; + + private const int DefaultMaxBrowserCacheDays = 30; + private const int DefaultSecureFilesMaxBrowserCacheDays = 0; + private const int DefaultMaxCacheDays = 365; + private const int DefaultMaxFileSize = 30_000_000; + + private const string DefaultAssetsPath = "Media"; + private const string DefaultAssetsUsersFolder = "_Users"; + private const string DefaultAssetsRequestPath = "/media"; + + private const bool DefaultUseTokenizedQueryString = true; + + // default-src self applied to prevent possible svg xss injection. + // style-src applied to allow browser behaviour of wrapping raw images in a styled img element. + private const string DefaultContentSecurityPolicy = "default-src 'self'; style-src 'unsafe-inline'"; + + private const int DefaultMaxUploadChunkSize = 104_857_600; // 100MB + + private static readonly TimeSpan _defaultTemporaryFileLifeTime = TimeSpan.FromHours(1); + + private readonly IShellConfiguration _shellConfiguration; + + public MediaOptionsConfiguration(IShellConfiguration shellConfiguration) { - private static readonly int[] _defaultSupportedSizes = [16, 32, 50, 100, 160, 240, 480, 600, 1024, 2048]; - - private static readonly string[] _defaultAllowedFileExtensions = [ - // Images - ".jpg", - ".jpeg", - ".png", - ".gif", - ".ico", - ".svg", - ".webp", - - // Documents - ".pdf", // (Portable Document Format; Adobe Acrobat) - ".doc", - ".docx", // (Microsoft Word Document) - ".ppt", - ".pptx", - ".pps", - ".ppsx", // (Microsoft PowerPoint Presentation) - ".odt", // (OpenDocument Text Document) - ".xls", - ".xlsx", // (Microsoft Excel Document) - ".psd", // (Adobe Photoshop Document) - - // Audio - ".mp3", - ".m4a", - ".ogg", - ".wav", - - // Video - ".mp4", - ".m4v", // (MPEG-4) - ".mov", // (QuickTime) - ".wmv", // (Windows Media Video) - ".avi", - ".mpg", - ".ogv", // (Ogg) - ".3gp", // (3GPP) - ]; - - private const int DefaultMaxBrowserCacheDays = 30; - private const int DefaultSecureFilesMaxBrowserCacheDays = 0; - private const int DefaultMaxCacheDays = 365; - private const int DefaultMaxFileSize = 30_000_000; - - private const string DefaultAssetsPath = "Media"; - private const string DefaultAssetsUsersFolder = "_Users"; - private const string DefaultAssetsRequestPath = "/media"; - - private const bool DefaultUseTokenizedQueryString = true; - - // default-src self applied to prevent possible svg xss injection. - // style-src applied to allow browser behaviour of wrapping raw images in a styled img element. - private const string DefaultContentSecurityPolicy = "default-src 'self'; style-src 'unsafe-inline'"; - - private const int DefaultMaxUploadChunkSize = 104_857_600; // 100MB - - private static readonly TimeSpan _defaultTemporaryFileLifeTime = TimeSpan.FromHours(1); - - private readonly IShellConfiguration _shellConfiguration; - - public MediaOptionsConfiguration(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + _shellConfiguration = shellConfiguration; + } - public void Configure(MediaOptions options) + public void Configure(MediaOptions options) + { + var section = _shellConfiguration.GetSection("OrchardCore_Media"); + + // Because IShellConfiguration treats arrays as key value pairs, we replace the array value, + // rather than letting Configure merge the default array with the appsettings value. + options.SupportedSizes = section.GetSection("SupportedSizes") + .Get()?.OrderBy(s => s).ToArray() ?? _defaultSupportedSizes; + + options.AllowedFileExtensions = new HashSet( + section.GetSection("AllowedFileExtensions").Get() ?? _defaultAllowedFileExtensions, + StringComparer.OrdinalIgnoreCase); + + options.MaxBrowserCacheDays = section.GetValue("MaxBrowserCacheDays", DefaultMaxBrowserCacheDays); + options.MaxSecureFilesBrowserCacheDays = section.GetValue("MaxSecureFilesBrowserCacheDays", DefaultSecureFilesMaxBrowserCacheDays); + options.MaxCacheDays = section.GetValue("MaxCacheDays", DefaultMaxCacheDays); + options.ResizedCacheMaxStale = section.GetValue(nameof(options.ResizedCacheMaxStale)); + options.RemoteCacheMaxStale = section.GetValue(nameof(options.RemoteCacheMaxStale)); + options.MaxFileSize = section.GetValue("MaxFileSize", DefaultMaxFileSize); + options.CdnBaseUrl = section.GetValue("CdnBaseUrl", string.Empty).TrimEnd('/').ToLower(); + options.AssetsRequestPath = section.GetValue("AssetsRequestPath", DefaultAssetsRequestPath); + options.AssetsPath = section.GetValue("AssetsPath", DefaultAssetsPath); + options.AssetsUsersFolder = section.GetValue("AssetsUsersFolder", DefaultAssetsUsersFolder); + options.UseTokenizedQueryString = section.GetValue("UseTokenizedQueryString", DefaultUseTokenizedQueryString); + options.MaxUploadChunkSize = section.GetValue(nameof(options.MaxUploadChunkSize), DefaultMaxUploadChunkSize); + options.TemporaryFileLifetime = section.GetValue(nameof(options.TemporaryFileLifetime), _defaultTemporaryFileLifeTime); + + var contentSecurityPolicy = section.GetValue("ContentSecurityPolicy", DefaultContentSecurityPolicy); + + // Use the same cache control header as ImageSharp does for resized images. + var cacheControl = "public, must-revalidate, max-age=" + TimeSpan.FromDays(options.MaxBrowserCacheDays).TotalSeconds.ToString(); + // Secure files are not cached at all. + var secureCacheControl = options.MaxSecureFilesBrowserCacheDays == 0 + ? "no-store" + : "public, must-revalidate, max-age=" + TimeSpan.FromDays(options.MaxSecureFilesBrowserCacheDays).TotalSeconds.ToString(); + + options.StaticFileOptions = new StaticFileOptions { - var section = _shellConfiguration.GetSection("OrchardCore_Media"); - - // Because IShellConfiguration treats arrays as key value pairs, we replace the array value, - // rather than letting Configure merge the default array with the appsettings value. - options.SupportedSizes = section.GetSection("SupportedSizes") - .Get()?.OrderBy(s => s).ToArray() ?? _defaultSupportedSizes; - - options.AllowedFileExtensions = new HashSet( - section.GetSection("AllowedFileExtensions").Get() ?? _defaultAllowedFileExtensions, - StringComparer.OrdinalIgnoreCase); - - options.MaxBrowserCacheDays = section.GetValue("MaxBrowserCacheDays", DefaultMaxBrowserCacheDays); - options.MaxSecureFilesBrowserCacheDays = section.GetValue("MaxSecureFilesBrowserCacheDays", DefaultSecureFilesMaxBrowserCacheDays); - options.MaxCacheDays = section.GetValue("MaxCacheDays", DefaultMaxCacheDays); - options.ResizedCacheMaxStale = section.GetValue(nameof(options.ResizedCacheMaxStale)); - options.RemoteCacheMaxStale = section.GetValue(nameof(options.RemoteCacheMaxStale)); - options.MaxFileSize = section.GetValue("MaxFileSize", DefaultMaxFileSize); - options.CdnBaseUrl = section.GetValue("CdnBaseUrl", string.Empty).TrimEnd('/').ToLower(); - options.AssetsRequestPath = section.GetValue("AssetsRequestPath", DefaultAssetsRequestPath); - options.AssetsPath = section.GetValue("AssetsPath", DefaultAssetsPath); - options.AssetsUsersFolder = section.GetValue("AssetsUsersFolder", DefaultAssetsUsersFolder); - options.UseTokenizedQueryString = section.GetValue("UseTokenizedQueryString", DefaultUseTokenizedQueryString); - options.MaxUploadChunkSize = section.GetValue(nameof(options.MaxUploadChunkSize), DefaultMaxUploadChunkSize); - options.TemporaryFileLifetime = section.GetValue(nameof(options.TemporaryFileLifetime), _defaultTemporaryFileLifeTime); - - var contentSecurityPolicy = section.GetValue("ContentSecurityPolicy", DefaultContentSecurityPolicy); - - // Use the same cache control header as ImageSharp does for resized images. - var cacheControl = "public, must-revalidate, max-age=" + TimeSpan.FromDays(options.MaxBrowserCacheDays).TotalSeconds.ToString(); - // Secure files are not cached at all. - var secureCacheControl = options.MaxSecureFilesBrowserCacheDays == 0 - ? "no-store" - : "public, must-revalidate, max-age=" + TimeSpan.FromDays(options.MaxSecureFilesBrowserCacheDays).TotalSeconds.ToString(); - - options.StaticFileOptions = new StaticFileOptions + RequestPath = options.AssetsRequestPath, + ServeUnknownFileTypes = true, + OnPrepareResponse = ctx => { - RequestPath = options.AssetsRequestPath, - ServeUnknownFileTypes = true, - OnPrepareResponse = ctx => - { - ctx.Context.Response.Headers.CacheControl = ctx.Context.IsSecureMediaRequested() ? secureCacheControl : cacheControl; - ctx.Context.Response.Headers.ContentSecurityPolicy = contentSecurityPolicy; - } - }; - } + ctx.Context.Response.Headers.CacheControl = ctx.Context.IsSecureMediaRequested() ? secureCacheControl : cacheControl; + ctx.Context.Response.Headers.ContentSecurityPolicy = contentSecurityPolicy; + } + }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfileService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfileService.cs index 07772fa1485..bca0a700d4d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfileService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfileService.cs @@ -2,62 +2,61 @@ using System.Threading.Tasks; using OrchardCore.Media.Processing; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class MediaProfileService : IMediaProfileService { - public class MediaProfileService : IMediaProfileService + private static readonly IDictionary _nullProfile = new Dictionary(); + private readonly MediaProfilesManager _mediaProfilesManager; + + public MediaProfileService(MediaProfilesManager mediaProfilesManager) { - private static readonly IDictionary _nullProfile = new Dictionary(); - private readonly MediaProfilesManager _mediaProfilesManager; + _mediaProfilesManager = mediaProfilesManager; + } - public MediaProfileService(MediaProfilesManager mediaProfilesManager) - { - _mediaProfilesManager = mediaProfilesManager; - } + public async Task> GetMediaProfileCommands(string name) + { + var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); - public async Task> GetMediaProfileCommands(string name) + if (mediaProfilesDocument.MediaProfiles.TryGetValue(name, out var mediaProfile)) { - var mediaProfilesDocument = await _mediaProfilesManager.GetMediaProfilesDocumentAsync(); + var commands = new Dictionary(); + if (mediaProfile.Width > 0) + { + commands["width"] = mediaProfile.Width.ToString(); + } - if (mediaProfilesDocument.MediaProfiles.TryGetValue(name, out var mediaProfile)) + if (mediaProfile.Height > 0) { - var commands = new Dictionary(); - if (mediaProfile.Width > 0) - { - commands["width"] = mediaProfile.Width.ToString(); - } - - if (mediaProfile.Height > 0) - { - commands["height"] = mediaProfile.Height.ToString(); - } - - if (mediaProfile.Mode != ResizeMode.Undefined) - { - commands["rmode"] = mediaProfile.Mode.ToString().ToLower(); - } - - if (mediaProfile.Format != Format.Undefined) - { - commands["format"] = mediaProfile.Format.ToString().ToLower(); - } - - if (mediaProfile.Quality > 0 && mediaProfile.Quality < 100) - { - commands["quality"] = mediaProfile.Quality.ToString(); - } - - if (!string.IsNullOrEmpty(mediaProfile.BackgroundColor)) - { - commands["bgcolor"] = mediaProfile.BackgroundColor; - } - - return commands; + commands["height"] = mediaProfile.Height.ToString(); } - else + + if (mediaProfile.Mode != ResizeMode.Undefined) { - return _nullProfile; + commands["rmode"] = mediaProfile.Mode.ToString().ToLower(); + } + if (mediaProfile.Format != Format.Undefined) + { + commands["format"] = mediaProfile.Format.ToString().ToLower(); } + + if (mediaProfile.Quality > 0 && mediaProfile.Quality < 100) + { + commands["quality"] = mediaProfile.Quality.ToString(); + } + + if (!string.IsNullOrEmpty(mediaProfile.BackgroundColor)) + { + commands["bgcolor"] = mediaProfile.BackgroundColor; + } + + return commands; + } + else + { + return _nullProfile; + } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfilesManager.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfilesManager.cs index 3524939724e..dcd85b152a1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfilesManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/MediaProfilesManager.cs @@ -2,36 +2,35 @@ using OrchardCore.Documents; using OrchardCore.Media.Models; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class MediaProfilesManager { - public class MediaProfilesManager - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public MediaProfilesManager(IDocumentManager documentManager) => _documentManager = documentManager; + public MediaProfilesManager(IDocumentManager documentManager) => _documentManager = documentManager; - /// - /// Loads the media profiles document from the store for updating and that should not be cached. - /// - public Task LoadMediaProfilesDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Loads the media profiles document from the store for updating and that should not be cached. + /// + public Task LoadMediaProfilesDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - /// Gets the media profiles document from the cache for sharing and that should not be updated. - /// - public Task GetMediaProfilesDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + /// Gets the media profiles document from the cache for sharing and that should not be updated. + /// + public Task GetMediaProfilesDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); - public async Task RemoveMediaProfileAsync(string name) - { - var document = await LoadMediaProfilesDocumentAsync(); - document.MediaProfiles.Remove(name); - await _documentManager.UpdateAsync(document); - } + public async Task RemoveMediaProfileAsync(string name) + { + var document = await LoadMediaProfilesDocumentAsync(); + document.MediaProfiles.Remove(name); + await _documentManager.UpdateAsync(document); + } - public async Task UpdateMediaProfileAsync(string name, MediaProfile mediaProfile) - { - var document = await LoadMediaProfilesDocumentAsync(); - document.MediaProfiles[name.ToLower()] = mediaProfile; - await _documentManager.UpdateAsync(document); - } + public async Task UpdateMediaProfileAsync(string name, MediaProfile mediaProfile) + { + var document = await LoadMediaProfilesDocumentAsync(); + document.MediaProfiles[name.ToLower()] = mediaProfile; + await _documentManager.UpdateAsync(document); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/NullMediaNameNormalizerService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/NullMediaNameNormalizerService.cs index 5baf5d4f5c7..53bf4f5becb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/NullMediaNameNormalizerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/NullMediaNameNormalizerService.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class NullMediaNameNormalizerService : IMediaNameNormalizerService { - public class NullMediaNameNormalizerService : IMediaNameNormalizerService - { - public string NormalizeFolderName(string folderName) => folderName; + public string NormalizeFolderName(string folderName) => folderName; - public string NormalizeFileName(string fileName) => fileName; - } + public string NormalizeFileName(string fileName) => fileName; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaExtensions.cs index 89136080bd6..c4bc09319f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaExtensions.cs @@ -1,22 +1,21 @@ -using System; +using System; using Microsoft.AspNetCore.Http; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +internal static class SecureMediaExtensions { - internal static class SecureMediaExtensions - { - private const string IsSecureMediaKey = "IsSecureMedia"; + private const string IsSecureMediaKey = "IsSecureMedia"; - public static bool IsSecureMediaEnabled(this IServiceProvider serviceProvider) - => serviceProvider.GetService(typeof(SecureMediaMarker)) is not null; + public static bool IsSecureMediaEnabled(this IServiceProvider serviceProvider) + => serviceProvider.GetService(typeof(SecureMediaMarker)) is not null; - public static bool IsSecureMediaEnabled(this HttpContext httpContext) - => httpContext.RequestServices.IsSecureMediaEnabled(); + public static bool IsSecureMediaEnabled(this HttpContext httpContext) + => httpContext.RequestServices.IsSecureMediaEnabled(); - public static bool IsSecureMediaRequested(this HttpContext httpContext) - => httpContext.Items.ContainsKey(IsSecureMediaKey); + public static bool IsSecureMediaRequested(this HttpContext httpContext) + => httpContext.Items.ContainsKey(IsSecureMediaKey); - public static void MarkAsSecureMediaRequested(this HttpContext httpContext) - => httpContext.Items[IsSecureMediaKey] = bool.TrueString; - } + public static void MarkAsSecureMediaRequested(this HttpContext httpContext) + => httpContext.Items[IsSecureMediaKey] = bool.TrueString; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMarker.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMarker.cs index 4e552bcfc01..2496468414c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMarker.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMarker.cs @@ -1,4 +1,3 @@ -namespace OrchardCore.Media.Services -{ - internal sealed class SecureMediaMarker { } -} +namespace OrchardCore.Media.Services; + +internal sealed class SecureMediaMarker { } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMiddleware.cs index 8ceb738c472..0cebf264f0e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/SecureMediaMiddleware.cs @@ -6,50 +6,49 @@ using Microsoft.Extensions.Options; using OrchardCore.Routing; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class SecureMediaMiddleware { - public class SecureMediaMiddleware + private readonly RequestDelegate _next; + private readonly PathString _assetsRequestPath; + + public SecureMediaMiddleware( + RequestDelegate next, + IOptions mediaOptions) { - private readonly RequestDelegate _next; - private readonly PathString _assetsRequestPath; + _next = next; + _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; + } - public SecureMediaMiddleware( - RequestDelegate next, - IOptions mediaOptions) + public async Task Invoke(HttpContext context, IAuthorizationService authorizationService, IAuthenticationService authenticationService) + { + var validateAssetsRequestPath = context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase, out var subPath); + if (!validateAssetsRequestPath) { - _next = next; - _assetsRequestPath = mediaOptions.Value.AssetsRequestPath; + await _next(context); + + return; } - public async Task Invoke(HttpContext context, IAuthorizationService authorizationService, IAuthenticationService authenticationService) + if (!(context.User.Identity?.IsAuthenticated ?? false)) { - var validateAssetsRequestPath = context.Request.Path.StartsWithNormalizedSegments(_assetsRequestPath, StringComparison.OrdinalIgnoreCase, out var subPath); - if (!validateAssetsRequestPath) - { - await _next(context); - - return; - } + // Allow bearer (API) authentication too. + var authenticateResult = await authenticationService.AuthenticateAsync(context, "Api"); - if (!(context.User.Identity?.IsAuthenticated ?? false)) + if (authenticateResult.Succeeded) { - // Allow bearer (API) authentication too. - var authenticateResult = await authenticationService.AuthenticateAsync(context, "Api"); - - if (authenticateResult.Succeeded) - { - context.User = authenticateResult.Principal; - } + context.User = authenticateResult.Principal; } + } - if (await authorizationService.AuthorizeAsync(context.User, SecureMediaPermissions.ViewMedia, (object)subPath.ToString())) - { - await _next(context); - } - else - { - context.Response.StatusCode = StatusCodes.Status404NotFound; - } + if (await authorizationService.AuthorizeAsync(context.User, SecureMediaPermissions.ViewMedia, (object)subPath.ToString())) + { + await _next(context); + } + else + { + context.Response.StatusCode = StatusCodes.Status404NotFound; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/SlugifyMediaNameNormalizerService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/SlugifyMediaNameNormalizerService.cs index b6f7697e364..9e092557ef9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/SlugifyMediaNameNormalizerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/SlugifyMediaNameNormalizerService.cs @@ -1,25 +1,24 @@ using System.IO; using OrchardCore.Modules.Services; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class SlugifyMediaNameNormalizerService : IMediaNameNormalizerService { - public class SlugifyMediaNameNormalizerService : IMediaNameNormalizerService - { - private readonly ISlugService _slugService; + private readonly ISlugService _slugService; - public SlugifyMediaNameNormalizerService(ISlugService slugService) - { - _slugService = slugService; - } + public SlugifyMediaNameNormalizerService(ISlugService slugService) + { + _slugService = slugService; + } - public string NormalizeFolderName(string folderName) - { - return _slugService.Slugify(folderName); - } + public string NormalizeFolderName(string folderName) + { + return _slugService.Slugify(folderName); + } - public string NormalizeFileName(string fileName) - { - return _slugService.Slugify(Path.GetFileNameWithoutExtension(fileName)) + Path.GetExtension(fileName); - } + public string NormalizeFileName(string fileName) + { + return _slugService.Slugify(Path.GetFileNameWithoutExtension(fileName)) + Path.GetExtension(fileName); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/TempDirCleanerService.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/TempDirCleanerService.cs index abcecb5b2f8..74362956365 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/TempDirCleanerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/TempDirCleanerService.cs @@ -4,55 +4,54 @@ using OrchardCore.Environment.Shell; using OrchardCore.Modules; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +public class TempDirCleanerService : ModularTenantEvents { - public class TempDirCleanerService : ModularTenantEvents - { - private readonly IMediaFileStore _fileStore; - private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; - private readonly ShellSettings _shellSettings; - private readonly ILogger _logger; + private readonly IMediaFileStore _fileStore; + private readonly AttachedMediaFieldFileService _attachedMediaFieldFileService; + private readonly ShellSettings _shellSettings; + private readonly ILogger _logger; - public TempDirCleanerService(IMediaFileStore fileStore, - AttachedMediaFieldFileService attachedMediaFieldFileService, - ShellSettings shellSettings, - ILogger logger) - { - _fileStore = fileStore; - _attachedMediaFieldFileService = attachedMediaFieldFileService; - _shellSettings = shellSettings; - _logger = logger; - } + public TempDirCleanerService(IMediaFileStore fileStore, + AttachedMediaFieldFileService attachedMediaFieldFileService, + ShellSettings shellSettings, + ILogger logger) + { + _fileStore = fileStore; + _attachedMediaFieldFileService = attachedMediaFieldFileService; + _shellSettings = shellSettings; + _logger = logger; + } - public override async Task ActivatedAsync() + public override async Task ActivatedAsync() + { + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + try { - try + var tempDir = _attachedMediaFieldFileService.MediaFieldsTempSubFolder; + + if (await _fileStore.GetDirectoryInfoAsync(tempDir) == null) { - var tempDir = _attachedMediaFieldFileService.MediaFieldsTempSubFolder; + return; + } - if (await _fileStore.GetDirectoryInfoAsync(tempDir) == null) - { - return; - } + await foreach (var c in _fileStore.GetDirectoryContentAsync(tempDir)) + { + var result = c.IsDirectory ? + await _fileStore.TryDeleteDirectoryAsync(c.Path) + : await _fileStore.TryDeleteFileAsync(c.Path); - await foreach (var c in _fileStore.GetDirectoryContentAsync(tempDir)) + if (!result) { - var result = c.IsDirectory ? - await _fileStore.TryDeleteDirectoryAsync(c.Path) - : await _fileStore.TryDeleteFileAsync(c.Path); - - if (!result) - { - _logger.LogWarning("Temporary entry {Path} could not be deleted.", c.Path); - } + _logger.LogWarning("Temporary entry {Path} could not be deleted.", c.Path); } } - catch (Exception e) - { - _logger.LogError(e, "An error occurred while cleaning temporary media folder."); - } + } + catch (Exception e) + { + _logger.LogError(e, "An error occurred while cleaning temporary media folder."); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Services/ViewMediaFolderAuthorizationHandler.cs b/src/OrchardCore.Modules/OrchardCore.Media/Services/ViewMediaFolderAuthorizationHandler.cs index f5dfe038346..e57fa97a929 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Services/ViewMediaFolderAuthorizationHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Services/ViewMediaFolderAuthorizationHandler.cs @@ -11,221 +11,220 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.Media.Services +namespace OrchardCore.Media.Services; + +/// +/// Checks if the user has related permission to view media in the path resource which is passed from AuthorizationHandler. +/// +public class ViewMediaFolderAuthorizationHandler : AuthorizationHandler { - /// - /// Checks if the user has related permission to view media in the path resource which is passed from AuthorizationHandler. - /// - public class ViewMediaFolderAuthorizationHandler : AuthorizationHandler + private const char PathSeparator = '/'; + + private static readonly ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity()); + + private readonly IServiceProvider _serviceProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IContentManager _contentManager; + private readonly IMediaFileStore _fileStore; + private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; + private readonly MediaOptions _mediaOptions; + private readonly string _mediaFieldsFolder; + private readonly string _usersFolder; + + public ViewMediaFolderAuthorizationHandler( + IServiceProvider serviceProvider, + IHttpContextAccessor httpContextAccessor, + AttachedMediaFieldFileService attachedMediaFieldFileService, + IMediaFileStore fileStore, + IOptions options, + IUserAssetFolderNameProvider userAssetFolderNameProvider, + IContentManager contentManager) { - private const char PathSeparator = '/'; - - private static readonly ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity()); - - private readonly IServiceProvider _serviceProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IContentManager _contentManager; - private readonly IMediaFileStore _fileStore; - private readonly IUserAssetFolderNameProvider _userAssetFolderNameProvider; - private readonly MediaOptions _mediaOptions; - private readonly string _mediaFieldsFolder; - private readonly string _usersFolder; - - public ViewMediaFolderAuthorizationHandler( - IServiceProvider serviceProvider, - IHttpContextAccessor httpContextAccessor, - AttachedMediaFieldFileService attachedMediaFieldFileService, - IMediaFileStore fileStore, - IOptions options, - IUserAssetFolderNameProvider userAssetFolderNameProvider, - IContentManager contentManager) + _serviceProvider = serviceProvider; + _httpContextAccessor = httpContextAccessor; + _fileStore = fileStore; + _userAssetFolderNameProvider = userAssetFolderNameProvider; + _contentManager = contentManager; + _mediaOptions = options.Value; + _mediaFieldsFolder = EnsureTrailingSlash(attachedMediaFieldFileService.MediaFieldsFolder); + _usersFolder = EnsureTrailingSlash(_mediaOptions.AssetsUsersFolder); + } + + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _serviceProvider = serviceProvider; - _httpContextAccessor = httpContextAccessor; - _fileStore = fileStore; - _userAssetFolderNameProvider = userAssetFolderNameProvider; - _contentManager = contentManager; - _mediaOptions = options.Value; - _mediaFieldsFolder = EnsureTrailingSlash(attachedMediaFieldFileService.MediaFieldsFolder); - _usersFolder = EnsureTrailingSlash(_mediaOptions.AssetsUsersFolder); + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + if (requirement.Permission.Name != SecureMediaPermissions.ViewMedia.Name) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } + return; + } - if (requirement.Permission.Name != SecureMediaPermissions.ViewMedia.Name) - { - return; - } + if (context.Resource is not string path) + { + return; + } + + path = Uri.UnescapeDataString(path); + + path = _fileStore.NormalizePath(path); - if (context.Resource is not string path) + // Permissions are only set for the root and the first folder tier. Only for users and + // media fields we will check sub folders too. + var i = path.IndexOf(PathSeparator); + var folderPath = i >= 0 ? path[..i] : path; + var directory = await _fileStore.GetDirectoryInfoAsync(folderPath); + if (directory is null && path.IndexOf(PathSeparator, folderPath.Length) < 0) + { + // This could be a new directory, or a new or existing file in the root folder. As we cannot directly determine + // whether a file is uploaded or a new directory is created, we will check against the list of allowed extensions. + // If none is matched, we assume a new directory is created, otherwise we will check the root access only. + // Note: The file path is currently not authorized during upload, only the folder is checked. Therefore checking + // the file extensions is not actually required, but let's leave this in case we add an authorization call later. + if (await _fileStore.GetFileInfoAsync(folderPath) is not null || + _mediaOptions.AllowedFileExtensions.Any(ext => path.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) { - return; + path = string.Empty; } + } - path = Uri.UnescapeDataString(path); + if (IsAuthorizedFolder("/", path)) + { + await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewRootMedia); - path = _fileStore.NormalizePath(path); + return; + } - // Permissions are only set for the root and the first folder tier. Only for users and - // media fields we will check sub folders too. - var i = path.IndexOf(PathSeparator); - var folderPath = i >= 0 ? path[..i] : path; - var directory = await _fileStore.GetDirectoryInfoAsync(folderPath); - if (directory is null && path.IndexOf(PathSeparator, folderPath.Length) < 0) - { - // This could be a new directory, or a new or existing file in the root folder. As we cannot directly determine - // whether a file is uploaded or a new directory is created, we will check against the list of allowed extensions. - // If none is matched, we assume a new directory is created, otherwise we will check the root access only. - // Note: The file path is currently not authorized during upload, only the folder is checked. Therefore checking - // the file extensions is not actually required, but let's leave this in case we add an authorization call later. - if (await _fileStore.GetFileInfoAsync(folderPath) is not null || - _mediaOptions.AllowedFileExtensions.Any(ext => path.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) - { - path = string.Empty; - } - } + if (IsAuthorizedFolder(_mediaFieldsFolder, path) || IsDescendantOfAuthorizedFolder(_mediaFieldsFolder, path)) + { + await AuthorizeAttachedMediaFieldsFolderAsync(context, requirement, path); - if (IsAuthorizedFolder("/", path)) - { - await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewRootMedia); + return; + } - return; - } + if (IsAuthorizedFolder(_usersFolder, path) || IsDescendantOfAuthorizedFolder(_usersFolder, path)) + { + await AuthorizeUsersFolderAsync(context, requirement, path); - if (IsAuthorizedFolder(_mediaFieldsFolder, path) || IsDescendantOfAuthorizedFolder(_mediaFieldsFolder, path)) - { - await AuthorizeAttachedMediaFieldsFolderAsync(context, requirement, path); + return; + } - return; - } + // Create a dynamic permission for the folder path. This allows to give access to a specific folders only. + var template = SecureMediaPermissions.ConvertToDynamicPermission(SecureMediaPermissions.ViewMedia); + if (template != null) + { + var permission = SecureMediaPermissions.CreateDynamicPermission(template, folderPath); + await AuthorizeAsync(context, requirement, permission); + } + else + { + // Not a secure file + context.Succeed(requirement); + } + } - if (IsAuthorizedFolder(_usersFolder, path) || IsDescendantOfAuthorizedFolder(_usersFolder, path)) - { - await AuthorizeUsersFolderAsync(context, requirement, path); + private async Task AuthorizeAttachedMediaFieldsFolderAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, string path) + { + var attachedMediaPathParts = path + .Substring(_mediaFieldsFolder.Length - 1) + .Split(PathSeparator, 3, StringSplitOptions.RemoveEmptyEntries); - return; - } + // Don't allow 'mediafields' directly. + if (attachedMediaPathParts.Length == 0) + return; + + if (string.Equals(attachedMediaPathParts[0], "temp", StringComparison.OrdinalIgnoreCase)) + { + // Authorize per-user temporary files + var userId = attachedMediaPathParts.Length > 1 ? attachedMediaPathParts[1] : null; + var userAssetsFolderName = EnsureTrailingSlash(_userAssetFolderNameProvider.GetUserAssetFolderName(context.User)); - // Create a dynamic permission for the folder path. This allows to give access to a specific folders only. - var template = SecureMediaPermissions.ConvertToDynamicPermission(SecureMediaPermissions.ViewMedia); - if (template != null) + if (IsAuthorizedFolder(userAssetsFolderName, userId)) { - var permission = SecureMediaPermissions.CreateDynamicPermission(template, folderPath); - await AuthorizeAsync(context, requirement, permission); + await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewOwnMedia); } else { - // Not a secure file - context.Succeed(requirement); + await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewOthersMedia); } } - - private async Task AuthorizeAttachedMediaFieldsFolderAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, string path) + else { - var attachedMediaPathParts = path - .Substring(_mediaFieldsFolder.Length - 1) - .Split(PathSeparator, 3, StringSplitOptions.RemoveEmptyEntries); - - // Don't allow 'mediafields' directly. - if (attachedMediaPathParts.Length == 0) - return; + // Authorize by using the content item permission. The user must have access to the content item to allow its media + // as well. + var contentItemId = attachedMediaPathParts.Length > 1 ? attachedMediaPathParts[1] : null; + var contentItem = !string.IsNullOrEmpty(contentItemId) ? await _contentManager.GetAsync(contentItemId) : null; - if (string.Equals(attachedMediaPathParts[0], "temp", StringComparison.OrdinalIgnoreCase)) - { - // Authorize per-user temporary files - var userId = attachedMediaPathParts.Length > 1 ? attachedMediaPathParts[1] : null; - var userAssetsFolderName = EnsureTrailingSlash(_userAssetFolderNameProvider.GetUserAssetFolderName(context.User)); - - if (IsAuthorizedFolder(userAssetsFolderName, userId)) - { - await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewOwnMedia); - } - else - { - await AuthorizeAsync(context, requirement, SecureMediaPermissions.ViewOthersMedia); - } - } - else + // Disallow if content item is not found or allowed + if (contentItem is not null) { - // Authorize by using the content item permission. The user must have access to the content item to allow its media - // as well. - var contentItemId = attachedMediaPathParts.Length > 1 ? attachedMediaPathParts[1] : null; - var contentItem = !string.IsNullOrEmpty(contentItemId) ? await _contentManager.GetAsync(contentItemId) : null; - - // Disallow if content item is not found or allowed - if (contentItem is not null) - { - await AuthorizeAsync(context, requirement, Contents.CommonPermissions.ViewContent, contentItem); - } + await AuthorizeAsync(context, requirement, Contents.CommonPermissions.ViewContent, contentItem); } } + } - private async Task AuthorizeUsersFolderAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, string path) + private async Task AuthorizeUsersFolderAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, string path) + { + // We need to allow the _Users folder for own media access too. If someone uploads into this folder, we are screwed. + Permission permission; + if (path.IndexOf(PathSeparator) < 0) { - // We need to allow the _Users folder for own media access too. If someone uploads into this folder, we are screwed. - Permission permission; - if (path.IndexOf(PathSeparator) < 0) - { - permission = SecureMediaPermissions.ViewOwnMedia; - } - else + permission = SecureMediaPermissions.ViewOwnMedia; + } + else + { + permission = SecureMediaPermissions.ViewOthersMedia; + + var userFolderName = _userAssetFolderNameProvider.GetUserAssetFolderName(context.User); + if (!string.IsNullOrEmpty(userFolderName)) { - permission = SecureMediaPermissions.ViewOthersMedia; + var userOwnFolder = EnsureTrailingSlash(_fileStore.Combine(_usersFolder, userFolderName)); - var userFolderName = _userAssetFolderNameProvider.GetUserAssetFolderName(context.User); - if (!string.IsNullOrEmpty(userFolderName)) + if (IsAuthorizedFolder(userOwnFolder, path) || IsDescendantOfAuthorizedFolder(userOwnFolder, path)) { - var userOwnFolder = EnsureTrailingSlash(_fileStore.Combine(_usersFolder, userFolderName)); - - if (IsAuthorizedFolder(userOwnFolder, path) || IsDescendantOfAuthorizedFolder(userOwnFolder, path)) - { - permission = SecureMediaPermissions.ViewOwnMedia; - } + permission = SecureMediaPermissions.ViewOwnMedia; } } - - await AuthorizeAsync(context, requirement, permission); } - private async Task AuthorizeAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, Permission permission, object resource = null) - { - var authorizationService = _serviceProvider.GetService(); - if (await authorizationService.AuthorizeAsync(context.User, permission, resource)) - { - // If anonymous access is also possible, we want to use default browser caching policies. - // Otherwise we set a marker which causes a different caching policy being used. - if ((context.User.Identity?.IsAuthenticated ?? false) && !await authorizationService.AuthorizeAsync(_anonymous, permission, resource)) - { - _httpContextAccessor.HttpContext.MarkAsSecureMediaRequested(); - } + await AuthorizeAsync(context, requirement, permission); + } - context.Succeed(requirement); - } - else + private async Task AuthorizeAsync(AuthorizationHandlerContext context, PermissionRequirement requirement, Permission permission, object resource = null) + { + var authorizationService = _serviceProvider.GetService(); + if (await authorizationService.AuthorizeAsync(context.User, permission, resource)) + { + // If anonymous access is also possible, we want to use default browser caching policies. + // Otherwise we set a marker which causes a different caching policy being used. + if ((context.User.Identity?.IsAuthenticated ?? false) && !await authorizationService.AuthorizeAsync(_anonymous, permission, resource)) { - // Note: We don't want other authorization handlers to succeed the requirement. This would allow access to the - // users and attached media field folders, e.g. if the anonymous role has the "ViewMedia" permission set. - context.Fail(new AuthorizationFailureReason(this, "View media permission not granted")); + _httpContextAccessor.HttpContext.MarkAsSecureMediaRequested(); } - } - private static bool IsAuthorizedFolder(string authorizedFolder, string childPath) + context.Succeed(requirement); + } + else { - // Ensure end trailing slash. childPath is already normalized. - childPath += PathSeparator; - - return childPath.Equals(authorizedFolder, StringComparison.OrdinalIgnoreCase); + // Note: We don't want other authorization handlers to succeed the requirement. This would allow access to the + // users and attached media field folders, e.g. if the anonymous role has the "ViewMedia" permission set. + context.Fail(new AuthorizationFailureReason(this, "View media permission not granted")); } + } - private static bool IsDescendantOfAuthorizedFolder(string authorizedFolder, string childPath) - => childPath.StartsWith(authorizedFolder, StringComparison.OrdinalIgnoreCase); + private static bool IsAuthorizedFolder(string authorizedFolder, string childPath) + { + // Ensure end trailing slash. childPath is already normalized. + childPath += PathSeparator; - private string EnsureTrailingSlash(string path) => _fileStore.NormalizePath(path) + PathSeparator; + return childPath.Equals(authorizedFolder, StringComparison.OrdinalIgnoreCase); } + + private static bool IsDescendantOfAuthorizedFolder(string authorizedFolder, string childPath) + => childPath.StartsWith(authorizedFolder, StringComparison.OrdinalIgnoreCase); + + private string EnsureTrailingSlash(string path) => _fileStore.NormalizePath(path) + PathSeparator; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Settings/MediaFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Media/Settings/MediaFieldSettingsDriver.cs index 96235657dfa..c03c041e79b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Settings/MediaFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Settings/MediaFieldSettingsDriver.cs @@ -13,99 +13,98 @@ using OrchardCore.Media.ViewModels; using OrchardCore.Mvc.ModelBinding; -namespace OrchardCore.Media.Settings +namespace OrchardCore.Media.Settings; + +public sealed class MediaFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class MediaFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - private readonly IContentTypeProvider _contentTypeProvider; - private readonly MediaOptions _mediaOptions; + private readonly IContentTypeProvider _contentTypeProvider; + private readonly MediaOptions _mediaOptions; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public MediaFieldSettingsDriver( - IContentTypeProvider contentTypeProvider, - IOptions mediaOptions, - IStringLocalizer stringLocalizer) - { - _contentTypeProvider = contentTypeProvider; - _mediaOptions = mediaOptions.Value; - S = stringLocalizer; - } + public MediaFieldSettingsDriver( + IContentTypeProvider contentTypeProvider, + IOptions mediaOptions, + IStringLocalizer stringLocalizer) + { + _contentTypeProvider = contentTypeProvider; + _mediaOptions = mediaOptions.Value; + S = stringLocalizer; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("MediaFieldSettings_Edit", model => { - return Initialize("MediaFieldSettings_Edit", model => - { - var settings = partFieldDefinition.GetSettings(); + var settings = partFieldDefinition.GetSettings(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.Multiple = settings.Multiple; - model.AllowMediaText = settings.AllowMediaText; - model.AllowAnchors = settings.AllowAnchors; - model.AllowAllDefaultMediaTypes = settings.AllowedExtensions == null || settings.AllowedExtensions.Length == 0; + model.Hint = settings.Hint; + model.Required = settings.Required; + model.Multiple = settings.Multiple; + model.AllowMediaText = settings.AllowMediaText; + model.AllowAnchors = settings.AllowAnchors; + model.AllowAllDefaultMediaTypes = settings.AllowedExtensions == null || settings.AllowedExtensions.Length == 0; - var items = new List(); - foreach (var extension in _mediaOptions.AllowedFileExtensions) + var items = new List(); + foreach (var extension in _mediaOptions.AllowedFileExtensions) + { + if (_contentTypeProvider.TryGetContentType(extension, out var contentType)) { - if (_contentTypeProvider.TryGetContentType(extension, out var contentType)) + var item = new MediaTypeViewModel() { - var item = new MediaTypeViewModel() - { - Extension = extension, - ContentType = contentType, - IsSelected = settings.AllowedExtensions != null && settings.AllowedExtensions.Contains(extension) - }; - - var index = contentType.IndexOf('/'); + Extension = extension, + ContentType = contentType, + IsSelected = settings.AllowedExtensions != null && settings.AllowedExtensions.Contains(extension) + }; - if (index > -1) - { - item.Type = contentType[..index]; - } + var index = contentType.IndexOf('/'); - items.Add(item); + if (index > -1) + { + item.Type = contentType[..index]; } + + items.Add(item); } - model.MediaTypes = items - .OrderBy(vm => vm.ContentType) - .ToArray(); - }).Location("Content"); - } + } + model.MediaTypes = items + .OrderBy(vm => vm.ContentType) + .ToArray(); + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new MediaFieldSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + var settings = new MediaFieldSettings() { - var model = new MediaFieldSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - var settings = new MediaFieldSettings() - { - Hint = model.Hint, - Required = model.Required, - Multiple = model.Multiple, - AllowMediaText = model.AllowMediaText, - AllowAnchors = model.AllowAnchors, - }; - - if (!model.AllowAllDefaultMediaTypes) - { - var selectedExtensions = model.MediaTypes.Where(vm => vm.IsSelected && _mediaOptions.AllowedFileExtensions.Contains(vm.Extension)) - .Select(x => x.Extension) - .ToArray(); - - if (selectedExtensions.Length == 0) - { - context.Updater.ModelState.AddModelError(Prefix, string.Empty, S["Please select at least one extension."]); - } + Hint = model.Hint, + Required = model.Required, + Multiple = model.Multiple, + AllowMediaText = model.AllowMediaText, + AllowAnchors = model.AllowAnchors, + }; - settings.AllowedExtensions = selectedExtensions; - } + if (!model.AllowAllDefaultMediaTypes) + { + var selectedExtensions = model.MediaTypes.Where(vm => vm.IsSelected && _mediaOptions.AllowedFileExtensions.Contains(vm.Extension)) + .Select(x => x.Extension) + .ToArray(); - if (context.Updater.ModelState.IsValid) + if (selectedExtensions.Length == 0) { - context.Builder.WithSettings(settings); + context.Updater.ModelState.AddModelError(Prefix, string.Empty, S["Please select at least one extension."]); } - return Edit(partFieldDefinition, context); + settings.AllowedExtensions = selectedExtensions; } + + if (context.Updater.ModelState.IsValid) + { + context.Builder.WithSettings(settings); + } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/AssetUrlShortcodeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/AssetUrlShortcodeProvider.cs index 89eaa596c25..c3d86df4c0e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/AssetUrlShortcodeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/AssetUrlShortcodeProvider.cs @@ -7,100 +7,99 @@ using OrchardCore.ResourceManagement; using Shortcodes; -namespace OrchardCore.Media.Shortcodes +namespace OrchardCore.Media.Shortcodes; + +public class AssetUrlShortcodeProvider : IShortcodeProvider { - public class AssetUrlShortcodeProvider : IShortcodeProvider + private readonly IMediaFileStore _mediaFileStore; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ResourceManagementOptions _options; + + public AssetUrlShortcodeProvider( + IMediaFileStore mediaFileStore, + IHttpContextAccessor httpContextAccessor, + IOptions options) + { + _mediaFileStore = mediaFileStore; + _httpContextAccessor = httpContextAccessor; + _options = options.Value; + } + + public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) { - private readonly IMediaFileStore _mediaFileStore; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ResourceManagementOptions _options; - - public AssetUrlShortcodeProvider( - IMediaFileStore mediaFileStore, - IHttpContextAccessor httpContextAccessor, - IOptions options) + if (!string.Equals(identifier, "asset_url", StringComparison.OrdinalIgnoreCase)) { - _mediaFileStore = mediaFileStore; - _httpContextAccessor = httpContextAccessor; - _options = options.Value; + return new ValueTask((string)null); } - public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + // Handle self closing shortcodes. + if (string.IsNullOrEmpty(content)) { - if (!string.Equals(identifier, "asset_url", StringComparison.OrdinalIgnoreCase)) - { - return new ValueTask((string)null); - } - - // Handle self closing shortcodes. + content = arguments.NamedOrDefault("src"); if (string.IsNullOrEmpty(content)) { - content = arguments.NamedOrDefault("src"); - if (string.IsNullOrEmpty(content)) - { - return new ValueTask("[asset_url]"); - } + return new ValueTask("[asset_url]"); } + } - if (!content.StartsWith("//", StringComparison.Ordinal) && !content.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + if (!content.StartsWith("//", StringComparison.Ordinal) && !content.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + // Serve static files from virtual path. + if (content.StartsWith("~/", StringComparison.Ordinal)) { - // Serve static files from virtual path. - if (content.StartsWith("~/", StringComparison.Ordinal)) + content = _httpContextAccessor.HttpContext.Request.PathBase.Add(content[1..]).Value; + if (!string.IsNullOrEmpty(_options.CdnBaseUrl)) { - content = _httpContextAccessor.HttpContext.Request.PathBase.Add(content[1..]).Value; - if (!string.IsNullOrEmpty(_options.CdnBaseUrl)) - { - content = _options.CdnBaseUrl + content; - } - } - else - { - content = content.RemoveQueryString(out var queryString); - content = _mediaFileStore.MapPathToPublicUrl(content) + queryString; + content = _options.CdnBaseUrl + content; } } - - if (arguments.Any()) + else { - var queryStringParams = new Dictionary(); - - var width = arguments.Named("width"); - var height = arguments.Named("height"); - var mode = arguments.Named("mode"); - var quality = arguments.Named("quality"); - var format = arguments.Named("format"); + content = content.RemoveQueryString(out var queryString); + content = _mediaFileStore.MapPathToPublicUrl(content) + queryString; + } + } - if (width != null) - { - queryStringParams.Add("width", width); - } + if (arguments.Any()) + { + var queryStringParams = new Dictionary(); - if (height != null) - { - queryStringParams.Add("height", height); - } + var width = arguments.Named("width"); + var height = arguments.Named("height"); + var mode = arguments.Named("mode"); + var quality = arguments.Named("quality"); + var format = arguments.Named("format"); - if (mode != null) - { - queryStringParams.Add("rmode", mode); - } + if (width != null) + { + queryStringParams.Add("width", width); + } - if (quality != null) - { - queryStringParams.Add("quality", quality); - } + if (height != null) + { + queryStringParams.Add("height", height); + } - if (format != null) - { - queryStringParams.Add("format", format); - } + if (mode != null) + { + queryStringParams.Add("rmode", mode); + } - content = QueryHelpers.AddQueryString(content, queryStringParams); + if (quality != null) + { + queryStringParams.Add("quality", quality); } - // This does not produce a tag, so sanitization is performed by the consumer (html body or markdown). + if (format != null) + { + queryStringParams.Add("format", format); + } - return new ValueTask(content); + content = QueryHelpers.AddQueryString(content, queryStringParams); } + + // This does not produce a tag, so sanitization is performed by the consumer (html body or markdown). + + return new ValueTask(content); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/ImageShortcodeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/ImageShortcodeProvider.cs index c01540a8c06..3f226a64754 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/ImageShortcodeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Shortcodes/ImageShortcodeProvider.cs @@ -9,135 +9,134 @@ using OrchardCore.ResourceManagement; using Shortcodes; -namespace OrchardCore.Media.Shortcodes +namespace OrchardCore.Media.Shortcodes; + +public class ImageShortcodeProvider : IShortcodeProvider { - public class ImageShortcodeProvider : IShortcodeProvider + private static ValueTask Null => new((string)null); + private static ValueTask ImageShortcode => new("[image]"); + private static readonly HashSet _shortcodes = new(StringComparer.OrdinalIgnoreCase) { - private static ValueTask Null => new((string)null); - private static ValueTask ImageShortcode => new("[image]"); - private static readonly HashSet _shortcodes = new(StringComparer.OrdinalIgnoreCase) - { - "image", - "media" // [media] is a deprecated shortcode, and can be removed in a future release. - }; - - private readonly IMediaFileStore _mediaFileStore; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ResourceManagementOptions _options; - private readonly IFileVersionProvider _fileVersionProvider; - - public ImageShortcodeProvider( - IMediaFileStore mediaFileStore, - IHtmlSanitizerService htmlSanitizerService, - IHttpContextAccessor httpContextAccessor, - IOptions options, - IFileVersionProvider fileVersionProvider) + "image", + "media" // [media] is a deprecated shortcode, and can be removed in a future release. + }; + + private readonly IMediaFileStore _mediaFileStore; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ResourceManagementOptions _options; + private readonly IFileVersionProvider _fileVersionProvider; + + public ImageShortcodeProvider( + IMediaFileStore mediaFileStore, + IHtmlSanitizerService htmlSanitizerService, + IHttpContextAccessor httpContextAccessor, + IOptions options, + IFileVersionProvider fileVersionProvider) + { + _mediaFileStore = mediaFileStore; + _htmlSanitizerService = htmlSanitizerService; + _httpContextAccessor = httpContextAccessor; + _options = options.Value; + _fileVersionProvider = fileVersionProvider; + } + + public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + { + if (!_shortcodes.Contains(identifier)) { - _mediaFileStore = mediaFileStore; - _htmlSanitizerService = htmlSanitizerService; - _httpContextAccessor = httpContextAccessor; - _options = options.Value; - _fileVersionProvider = fileVersionProvider; + return Null; } - public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + // Handle self closing shortcodes. + if (string.IsNullOrEmpty(content)) { - if (!_shortcodes.Contains(identifier)) + content = arguments.NamedOrDefault("src"); + if (string.IsNullOrEmpty(content)) { - return Null; + // Do not handle the deprecated media shortcode in this edge case. + return ImageShortcode; } + } - // Handle self closing shortcodes. - if (string.IsNullOrEmpty(content)) + if (!content.StartsWith("//", StringComparison.Ordinal) && !content.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + // Serve static files from virtual path. + if (content.StartsWith("~/", StringComparison.Ordinal)) { - content = arguments.NamedOrDefault("src"); - if (string.IsNullOrEmpty(content)) + content = _httpContextAccessor.HttpContext.Request.PathBase.Add(content[1..]).Value; + if (!string.IsNullOrEmpty(_options.CdnBaseUrl)) { - // Do not handle the deprecated media shortcode in this edge case. - return ImageShortcode; + content = _options.CdnBaseUrl + content; } } - - if (!content.StartsWith("//", StringComparison.Ordinal) && !content.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + else { - // Serve static files from virtual path. - if (content.StartsWith("~/", StringComparison.Ordinal)) - { - content = _httpContextAccessor.HttpContext.Request.PathBase.Add(content[1..]).Value; - if (!string.IsNullOrEmpty(_options.CdnBaseUrl)) - { - content = _options.CdnBaseUrl + content; - } - } - else - { - content = content.RemoveQueryString(out var queryString); - content = _mediaFileStore.MapPathToPublicUrl(content) + queryString; - } + content = content.RemoveQueryString(out var queryString); + content = _mediaFileStore.MapPathToPublicUrl(content) + queryString; } - var className = string.Empty; - var altText = string.Empty; - if (arguments.Any()) + } + var className = string.Empty; + var altText = string.Empty; + if (arguments.Any()) + { + var queryStringParams = new Dictionary(); + + var width = arguments.Named("width"); + var height = arguments.Named("height"); + var mode = arguments.Named("mode"); + var quality = arguments.Named("quality"); + var format = arguments.Named("format"); + var appendVersion = arguments.Named("append_version"); + className = arguments.Named("class"); + altText = arguments.Named("alt"); + + if (width != null) { - var queryStringParams = new Dictionary(); - - var width = arguments.Named("width"); - var height = arguments.Named("height"); - var mode = arguments.Named("mode"); - var quality = arguments.Named("quality"); - var format = arguments.Named("format"); - var appendVersion = arguments.Named("append_version"); - className = arguments.Named("class"); - altText = arguments.Named("alt"); - - if (width != null) - { - queryStringParams.Add("width", width); - } - - if (height != null) - { - queryStringParams.Add("height", height); - } - - if (mode != null) - { - queryStringParams.Add("rmode", mode); - } + queryStringParams.Add("width", width); + } - if (quality != null) - { - queryStringParams.Add("quality", quality); - } + if (height != null) + { + queryStringParams.Add("height", height); + } - if (format != null) - { - queryStringParams.Add("format", format); - } + if (mode != null) + { + queryStringParams.Add("rmode", mode); + } - if (appendVersion?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false) - { - content = _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, content); - } + if (quality != null) + { + queryStringParams.Add("quality", quality); + } - if (className != null) - { - className = "class=\"" + className + "\" "; - } + if (format != null) + { + queryStringParams.Add("format", format); + } - if (altText != null) - { - altText = "alt=\"" + altText + "\" "; - } + if (appendVersion?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false) + { + content = _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, content); + } - content = QueryHelpers.AddQueryString(content, queryStringParams); + if (className != null) + { + className = "class=\"" + className + "\" "; } - content = ""; - content = _htmlSanitizerService.Sanitize(content); + if (altText != null) + { + altText = "alt=\"" + altText + "\" "; + } - return new ValueTask(content); + content = QueryHelpers.AddQueryString(content, queryStringParams); } + + content = ""; + content = _htmlSanitizerService.Sanitize(content); + + return new ValueTask(content); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Media/Startup.cs index 7e71d267ffc..7af369ae1fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/Startup.cs @@ -51,247 +51,247 @@ using SixLabors.ImageSharp.Web.Middleware; using SixLabors.ImageSharp.Web.Providers; -namespace OrchardCore.Media +namespace OrchardCore.Media; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override int Order + => OrchardCoreConstants.ConfigureOrder.Media; + + private const string ImageSharpCacheFolder = "is-cache"; + + private readonly ShellSettings _shellSettings; + + public Startup(ShellSettings shellSettings) { - public override int Order - => OrchardCoreConstants.ConfigureOrder.Media; + _shellSettings = shellSettings; + } - private const string ImageSharpCacheFolder = "is-cache"; + public override void ConfigureServices(IServiceCollection services) + { + services.AddHttpClient(); - private readonly ShellSettings _shellSettings; + services.AddSingleton(); - public Startup(ShellSettings shellSettings) - { - _shellSettings = shellSettings; - } + // Resized media and remote media caches cleanups. + services.AddSingleton(); + services.AddSingleton(); - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.AddHttpClient(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); - services.AddSingleton(); + o.Filters.AddFilter("img_tag", MediaFilters.ImgTag); + }) + .AddLiquidFilter("asset_url") + .AddLiquidFilter("resize_url"); - // Resized media and remote media caches cleanups. - services.AddSingleton(); - services.AddSingleton(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - - o.Filters.AddFilter("img_tag", MediaFilters.ImgTag); - }) - .AddLiquidFilter("asset_url") - .AddLiquidFilter("resize_url"); + services.AddTransient, MediaOptionsConfiguration>(); - services.AddTransient, ResourceManagementOptionsConfiguration>(); + services.AddSingleton(serviceProvider => + { + var shellOptions = serviceProvider.GetRequiredService>(); + var shellSettings = serviceProvider.GetRequiredService(); + var options = serviceProvider.GetRequiredService>().Value; - services.AddTransient, MediaOptionsConfiguration>(); + var mediaPath = GetMediaPath(shellOptions.Value, shellSettings, options.AssetsPath); - services.AddSingleton(serviceProvider => + if (!Directory.Exists(mediaPath)) { - var shellOptions = serviceProvider.GetRequiredService>(); - var shellSettings = serviceProvider.GetRequiredService(); - var options = serviceProvider.GetRequiredService>().Value; + Directory.CreateDirectory(mediaPath); + } - var mediaPath = GetMediaPath(shellOptions.Value, shellSettings, options.AssetsPath); + return new MediaFileProvider(options.AssetsRequestPath, mediaPath); + }); - if (!Directory.Exists(mediaPath)) - { - Directory.CreateDirectory(mediaPath); - } + services.AddSingleton(serviceProvider => + serviceProvider.GetRequiredService() + ); - return new MediaFileProvider(options.AssetsRequestPath, mediaPath); - }); + services.AddSingleton(serviceProvider => + { + var shellOptions = serviceProvider.GetRequiredService>(); + var shellSettings = serviceProvider.GetRequiredService(); + var mediaOptions = serviceProvider.GetRequiredService>().Value; + var mediaEventHandlers = serviceProvider.GetServices(); + var mediaCreatingEventHandlers = serviceProvider.GetServices(); + var logger = serviceProvider.GetRequiredService>(); - services.AddSingleton(serviceProvider => - serviceProvider.GetRequiredService() - ); + var mediaPath = GetMediaPath(shellOptions.Value, shellSettings, mediaOptions.AssetsPath); + var fileStore = new FileSystemStore(mediaPath); - services.AddSingleton(serviceProvider => - { - var shellOptions = serviceProvider.GetRequiredService>(); - var shellSettings = serviceProvider.GetRequiredService(); - var mediaOptions = serviceProvider.GetRequiredService>().Value; - var mediaEventHandlers = serviceProvider.GetServices(); - var mediaCreatingEventHandlers = serviceProvider.GetServices(); - var logger = serviceProvider.GetRequiredService>(); - - var mediaPath = GetMediaPath(shellOptions.Value, shellSettings, mediaOptions.AssetsPath); - var fileStore = new FileSystemStore(mediaPath); - - var mediaUrlBase = "/" + fileStore.Combine(shellSettings.RequestUrlPrefix, mediaOptions.AssetsRequestPath); - - var originalPathBase = serviceProvider.GetRequiredService().HttpContext - ?.Features.Get() - ?.OriginalPathBase ?? PathString.Empty; - - if (originalPathBase.HasValue) - { - mediaUrlBase = fileStore.Combine(originalPathBase.Value, mediaUrlBase); - } - - return new DefaultMediaFileStore(fileStore, mediaUrlBase, mediaOptions.CdnBaseUrl, mediaEventHandlers, mediaCreatingEventHandlers, logger); - }); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // ImageSharp - - // Add ImageSharp Configuration first, to override ImageSharp defaults. - services.AddTransient, MediaImageSharpConfiguration>(); - - services - .AddImageSharp() - .RemoveProvider() - // For multitenancy we must use an absolute path to prevent leakage across tenants on different hosts. - .SetCacheKey() - .Configure(options => - { - options.CacheFolder = $"{_shellSettings.Name}/{ImageSharpCacheFolder}"; - options.CacheFolderDepth = 12; - }) - .AddProvider() - .AddProcessor() - .AddProcessor(); - - services.AddScoped(); - services.AddSingleton(); - services.AddTransient, MediaTokenOptionsConfiguration>(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); - - // Media Field - services.AddContentField() - .UseDisplayDriver(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddDataMigration(); - services.AddRecipeExecutionStep(); - - // MIME types - services.TryAddSingleton(); - - services.AddTagHelpers(); - services.AddTagHelpers(); - services.AddTagHelpers(); - - // Media Profiles - services.AddScoped(); - services.AddScoped(); - services.AddRecipeExecutionStep(); - - // Media Name Normalizer - services.AddScoped(); - - services.AddScoped(); - services.AddSingleton(); - services.AddSingleton(); - } + var mediaUrlBase = "/" + fileStore.Combine(shellSettings.RequestUrlPrefix, mediaOptions.AssetsRequestPath); - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - var mediaFileProvider = serviceProvider.GetRequiredService(); - var mediaOptions = serviceProvider.GetRequiredService>().Value; - var mediaFileStoreCache = serviceProvider.GetService(); + var originalPathBase = serviceProvider.GetRequiredService().HttpContext + ?.Features.Get() + ?.OriginalPathBase ?? PathString.Empty; - // Move middleware into SecureMediaStartup if it is possible to insert it between the users and media - // module. See issue https://github.com/OrchardCMS/OrchardCore/issues/15716. - // Secure media file middleware, but only if the feature is enabled. - if (serviceProvider.IsSecureMediaEnabled()) + if (originalPathBase.HasValue) { - app.UseMiddleware(); + mediaUrlBase = fileStore.Combine(originalPathBase.Value, mediaUrlBase); } - // FileStore middleware before ImageSharp, but only if a remote storage module has registered a cache provider. - if (mediaFileStoreCache != null) - { - app.UseMiddleware(); - } + return new DefaultMediaFileStore(fileStore, mediaUrlBase, mediaOptions.CdnBaseUrl, mediaEventHandlers, mediaCreatingEventHandlers, logger); + }); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // ImageSharp before the static file provider. - app.UseImageSharp(); + // ImageSharp - // The file provider is a circular dependency and replaceable via di. - mediaOptions.StaticFileOptions.FileProvider = mediaFileProvider; + // Add ImageSharp Configuration first, to override ImageSharp defaults. + services.AddTransient, MediaImageSharpConfiguration>(); + + services + .AddImageSharp() + .RemoveProvider() + // For multitenancy we must use an absolute path to prevent leakage across tenants on different hosts. + .SetCacheKey() + .Configure(options => + { + options.CacheFolder = $"{_shellSettings.Name}/{ImageSharpCacheFolder}"; + options.CacheFolderDepth = 12; + }) + .AddProvider() + .AddProcessor() + .AddProcessor(); + + services.AddScoped(); + services.AddSingleton(); + services.AddTransient, MediaTokenOptionsConfiguration>(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); + + // Media Field + services.AddContentField() + .UseDisplayDriver(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddDataMigration(); + services.AddRecipeExecutionStep(); + + // MIME types + services.TryAddSingleton(); + + services.AddTagHelpers(); + services.AddTagHelpers(); + services.AddTagHelpers(); + + // Media Profiles + services.AddScoped(); + services.AddScoped(); + services.AddRecipeExecutionStep(); + + // Media Name Normalizer + services.AddScoped(); + + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + } - // Use services.PostConfigure() to alter the media static file options event handlers. - app.UseStaticFiles(mediaOptions.StaticFileOptions); + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var mediaFileProvider = serviceProvider.GetRequiredService(); + var mediaOptions = serviceProvider.GetRequiredService>().Value; + var mediaFileStoreCache = serviceProvider.GetService(); + + // Move middleware into SecureMediaStartup if it is possible to insert it between the users and media + // module. See issue https://github.com/OrchardCMS/OrchardCore/issues/15716. + // Secure media file middleware, but only if the feature is enabled. + if (serviceProvider.IsSecureMediaEnabled()) + { + app.UseMiddleware(); } - private static string GetMediaPath(ShellOptions shellOptions, ShellSettings shellSettings, string assetsPath) + // FileStore middleware before ImageSharp, but only if a remote storage module has registered a cache provider. + if (mediaFileStoreCache != null) { - return PathExtensions.Combine(shellOptions.ShellsApplicationDataPath, shellOptions.ShellsContainerName, shellSettings.Name, assetsPath); + app.UseMiddleware(); } + + // ImageSharp before the static file provider. + app.UseImageSharp(); + + // The file provider is a circular dependency and replaceable via di. + mediaOptions.StaticFileOptions.FileProvider = mediaFileProvider; + + // Use services.PostConfigure() to alter the media static file options event handlers. + app.UseStaticFiles(mediaOptions.StaticFileOptions); } - [Feature("OrchardCore.Media.Cache")] - public sealed class MediaCacheStartup : StartupBase + private static string GetMediaPath(ShellOptions shellOptions, ShellSettings shellSettings, string assetsPath) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } + return PathExtensions.Combine(shellOptions.ShellsApplicationDataPath, shellOptions.ShellsContainerName, shellSettings.Name, assetsPath); } +} - [Feature("OrchardCore.Media.Slugify")] - public sealed class MediaSlugifyStartup : StartupBase +[Feature("OrchardCore.Media.Cache")] +public sealed class MediaCacheStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Media Name Normalizer - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[Feature("OrchardCore.Media.Slugify")] +public sealed class MediaSlugifyStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddDeployment(); - } + // Media Name Normalizer + services.AddScoped(); } +} - [Feature("OrchardCore.Media.Indexing")] - public sealed class MediaIndexingStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - } + services.AddDeployment(); + services.AddDeployment(); } +} - [Feature("OrchardCore.Media.Indexing.Text")] - public sealed class TextIndexingStartup : StartupBase +[Feature("OrchardCore.Media.Indexing")] +public sealed class MediaIndexingStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddMediaFileTextProvider(".txt"); - services.AddMediaFileTextProvider(".md"); - } + services.AddScoped(); + } +} + +[Feature("OrchardCore.Media.Indexing.Text")] +public sealed class TextIndexingStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddMediaFileTextProvider(".txt"); + services.AddMediaFileTextProvider(".md"); } +} - [RequireFeatures("OrchardCore.Shortcodes")] - public sealed class ShortcodesStartup : StartupBase +[RequireFeatures("OrchardCore.Shortcodes")] +public sealed class ShortcodesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + // Only add image as a descriptor as [media] is deprecated. + services.AddShortcode("image", d => { - // Only add image as a descriptor as [media] is deprecated. - services.AddShortcode("image", d => - { - d.DefaultValue = "[image] [/image]"; - d.Hint = "Add a image from the media library."; - d.Usage = + d.DefaultValue = "[image] [/image]"; + d.Hint = "Add a image from the media library."; + d.Usage = @"[image]foo.jpg[/image]
@@ -303,14 +303,14 @@ public override void ConfigureServices(IServiceCollection services)
class, alt
"; - d.Categories = ["HTML Content", "Media"]; - }); + d.Categories = ["HTML Content", "Media"]; + }); - services.AddShortcode("asset_url", d => - { - d.DefaultValue = "[asset_url] [/asset_url]"; - d.Hint = "Return a url from the media library."; - d.Usage = + services.AddShortcode("asset_url", d => + { + d.DefaultValue = "[asset_url] [/asset_url]"; + d.Hint = "Return a url from the media library."; + d.Usage = @"[asset_url]foo.jpg[/asset_url]
@@ -318,22 +318,21 @@ public override void ConfigureServices(IServiceCollection services)
width, height, mode
"; - d.Categories = ["HTML Content", "Media"]; - }); - } + d.Categories = ["HTML Content", "Media"]; + }); } +} - [Feature("OrchardCore.Media.Security")] - public sealed class SecureMediaStartup : StartupBase +[Feature("OrchardCore.Media.Security")] +public sealed class SecureMediaStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Marker service to easily detect if the feature has been enabled. - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + // Marker service to easily detect if the feature has been enabled. + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); - services.AddSingleton(); - } + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/AnchorTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/AnchorTagHelper.cs index 8826a27997f..30ca2d40fcc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/AnchorTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/AnchorTagHelper.cs @@ -2,62 +2,61 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace OrchardCore.Media.TagHelpers +namespace OrchardCore.Media.TagHelpers; + +[HtmlTargetElement(AnchorTagName, Attributes = AssetHrefAttributeName)] +public class AnchorTagHelper : TagHelper { - [HtmlTargetElement(AnchorTagName, Attributes = AssetHrefAttributeName)] - public class AnchorTagHelper : TagHelper + private const string AssetHrefAttributeName = "asset-href"; + private const string AnchorTagName = "a"; + private const string HrefAttributeName = "href"; + private const string AppendVersionAttributeName = "asp-append-version"; + + private readonly IMediaFileStore _mediaFileStore; + private readonly IFileVersionProvider _fileVersionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + + public override int Order => -10; + + [HtmlAttributeName(AssetHrefAttributeName)] + public string AssetHref { get; set; } + + /// + /// Value indicating if file version should be appended to href url. + /// + /// + /// If true then a query string "v" with the encoded content of the file is added. + /// + [HtmlAttributeName(AppendVersionAttributeName)] + public bool AppendVersion { get; set; } + + public AnchorTagHelper( + IMediaFileStore mediaFileStore, + IHttpContextAccessor httpContextAccessor, + IFileVersionProvider fileVersionProvider + ) + { + _mediaFileStore = mediaFileStore; + _httpContextAccessor = httpContextAccessor; + _fileVersionProvider = fileVersionProvider; + } + + public override void Process(TagHelperContext context, TagHelperOutput output) { - private const string AssetHrefAttributeName = "asset-href"; - private const string AnchorTagName = "a"; - private const string HrefAttributeName = "href"; - private const string AppendVersionAttributeName = "asp-append-version"; - - private readonly IMediaFileStore _mediaFileStore; - private readonly IFileVersionProvider _fileVersionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - - public override int Order => -10; - - [HtmlAttributeName(AssetHrefAttributeName)] - public string AssetHref { get; set; } - - /// - /// Value indicating if file version should be appended to href url. - /// - /// - /// If true then a query string "v" with the encoded content of the file is added. - /// - [HtmlAttributeName(AppendVersionAttributeName)] - public bool AppendVersion { get; set; } - - public AnchorTagHelper( - IMediaFileStore mediaFileStore, - IHttpContextAccessor httpContextAccessor, - IFileVersionProvider fileVersionProvider - ) + if (string.IsNullOrEmpty(AssetHref)) { - _mediaFileStore = mediaFileStore; - _httpContextAccessor = httpContextAccessor; - _fileVersionProvider = fileVersionProvider; + return; } - public override void Process(TagHelperContext context, TagHelperOutput output) + var resolvedUrl = _mediaFileStore != null ? _mediaFileStore.MapPathToPublicUrl(AssetHref) : AssetHref; + + if (AppendVersion && _fileVersionProvider != null) + { + output.Attributes.SetAttribute(HrefAttributeName, _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl)); + } + else { - if (string.IsNullOrEmpty(AssetHref)) - { - return; - } - - var resolvedUrl = _mediaFileStore != null ? _mediaFileStore.MapPathToPublicUrl(AssetHref) : AssetHref; - - if (AppendVersion && _fileVersionProvider != null) - { - output.Attributes.SetAttribute(HrefAttributeName, _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl)); - } - else - { - output.Attributes.SetAttribute(HrefAttributeName, resolvedUrl); - } + output.Attributes.SetAttribute(HrefAttributeName, resolvedUrl); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageResizeTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageResizeTagHelper.cs index 828abd326b0..eb6ea7f64af 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageResizeTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageResizeTagHelper.cs @@ -6,96 +6,95 @@ using OrchardCore.Media.Processing; using OrchardCore.Media.Services; -namespace OrchardCore.Media.TagHelpers +namespace OrchardCore.Media.TagHelpers; + +[HtmlTargetElement("img", Attributes = ImageSizeWidthAttributeName)] +[HtmlTargetElement("img", Attributes = ImageSizeHeightAttributeName)] +[HtmlTargetElement("img", Attributes = ImageProfileAttributeName)] +[HtmlTargetElement("img", Attributes = ImageSizeWidthAttributeName + "," + ImageSizeModeAttributeName)] +[HtmlTargetElement("img", Attributes = ImageSizeHeightAttributeName + "," + ImageSizeModeAttributeName)] +public class ImageResizeTagHelper : TagHelper { - [HtmlTargetElement("img", Attributes = ImageSizeWidthAttributeName)] - [HtmlTargetElement("img", Attributes = ImageSizeHeightAttributeName)] - [HtmlTargetElement("img", Attributes = ImageProfileAttributeName)] - [HtmlTargetElement("img", Attributes = ImageSizeWidthAttributeName + "," + ImageSizeModeAttributeName)] - [HtmlTargetElement("img", Attributes = ImageSizeHeightAttributeName + "," + ImageSizeModeAttributeName)] - public class ImageResizeTagHelper : TagHelper - { - private const string ImageSizeAttributePrefix = "img-"; - private const string ImageSizeWidthAttributeName = ImageSizeAttributePrefix + "width"; - private const string ImageSizeHeightAttributeName = ImageSizeAttributePrefix + "height"; - private const string ImageSizeModeAttributeName = ImageSizeAttributePrefix + "resize-mode"; - private const string ImageQualityAttributeName = ImageSizeAttributePrefix + "quality"; - private const string ImageFormatAttributeName = ImageSizeAttributePrefix + "format"; - private const string ImageProfileAttributeName = ImageSizeAttributePrefix + "profile"; - private const string ImageAnchorAttributeName = ImageSizeAttributePrefix + "anchor"; - private const string ImageBackgroundColorAttributeName = ImageSizeAttributePrefix + "bgcolor"; + private const string ImageSizeAttributePrefix = "img-"; + private const string ImageSizeWidthAttributeName = ImageSizeAttributePrefix + "width"; + private const string ImageSizeHeightAttributeName = ImageSizeAttributePrefix + "height"; + private const string ImageSizeModeAttributeName = ImageSizeAttributePrefix + "resize-mode"; + private const string ImageQualityAttributeName = ImageSizeAttributePrefix + "quality"; + private const string ImageFormatAttributeName = ImageSizeAttributePrefix + "format"; + private const string ImageProfileAttributeName = ImageSizeAttributePrefix + "profile"; + private const string ImageAnchorAttributeName = ImageSizeAttributePrefix + "anchor"; + private const string ImageBackgroundColorAttributeName = ImageSizeAttributePrefix + "bgcolor"; - private readonly IMediaProfileService _mediaProfileService; - private readonly MediaOptions _mediaOptions; - private readonly IMediaTokenService _mediaTokenService; + private readonly IMediaProfileService _mediaProfileService; + private readonly MediaOptions _mediaOptions; + private readonly IMediaTokenService _mediaTokenService; - [HtmlAttributeName(ImageSizeWidthAttributeName)] - public int? ImageWidth { get; set; } + [HtmlAttributeName(ImageSizeWidthAttributeName)] + public int? ImageWidth { get; set; } - [HtmlAttributeName(ImageSizeHeightAttributeName)] - public int? ImageHeight { get; set; } + [HtmlAttributeName(ImageSizeHeightAttributeName)] + public int? ImageHeight { get; set; } - [HtmlAttributeName(ImageQualityAttributeName)] - public int? ImageQuality { get; set; } + [HtmlAttributeName(ImageQualityAttributeName)] + public int? ImageQuality { get; set; } - [HtmlAttributeName(ImageSizeModeAttributeName)] - public ResizeMode ResizeMode { get; set; } + [HtmlAttributeName(ImageSizeModeAttributeName)] + public ResizeMode ResizeMode { get; set; } - [HtmlAttributeName(ImageFormatAttributeName)] - public Format ImageFormat { get; set; } + [HtmlAttributeName(ImageFormatAttributeName)] + public Format ImageFormat { get; set; } - [HtmlAttributeName(ImageProfileAttributeName)] - public string ImageProfile { get; set; } + [HtmlAttributeName(ImageProfileAttributeName)] + public string ImageProfile { get; set; } - [HtmlAttributeName(ImageAnchorAttributeName)] - public Anchor ImageAnchor { get; set; } + [HtmlAttributeName(ImageAnchorAttributeName)] + public Anchor ImageAnchor { get; set; } - [HtmlAttributeName(ImageBackgroundColorAttributeName)] - public string ImageBackgroundColor { get; set; } + [HtmlAttributeName(ImageBackgroundColorAttributeName)] + public string ImageBackgroundColor { get; set; } - [HtmlAttributeName("src")] - public string Src { get; set; } + [HtmlAttributeName("src")] + public string Src { get; set; } - public ImageResizeTagHelper( - IMediaProfileService mediaProfileService, - IOptions mediaOptions, - IMediaTokenService mediaTokenService) - { - _mediaProfileService = mediaProfileService; - _mediaOptions = mediaOptions.Value; - _mediaTokenService = mediaTokenService; - } + public ImageResizeTagHelper( + IMediaProfileService mediaProfileService, + IOptions mediaOptions, + IMediaTokenService mediaTokenService) + { + _mediaProfileService = mediaProfileService; + _mediaOptions = mediaOptions.Value; + _mediaTokenService = mediaTokenService; + } - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + if (!ImageWidth.HasValue && !ImageHeight.HasValue && string.IsNullOrEmpty(ImageProfile)) { - if (!ImageWidth.HasValue && !ImageHeight.HasValue && string.IsNullOrEmpty(ImageProfile)) - { - return; - } - - var imgSrc = output.Attributes["src"]?.Value.ToString() ?? Src; + return; + } - if (string.IsNullOrEmpty(imgSrc)) - { - return; - } + var imgSrc = output.Attributes["src"]?.Value.ToString() ?? Src; - IDictionary queryStringParams = null; + if (string.IsNullOrEmpty(imgSrc)) + { + return; + } - if (!string.IsNullOrEmpty(ImageProfile)) - { - queryStringParams = await _mediaProfileService.GetMediaProfileCommands(ImageProfile); - } + IDictionary queryStringParams = null; - var resizedSrc = ImageSharpUrlFormatter.GetImageResizeUrl(imgSrc, queryStringParams, ImageWidth, ImageHeight, ResizeMode, ImageQuality, ImageFormat, ImageAnchor, ImageBackgroundColor); + if (!string.IsNullOrEmpty(ImageProfile)) + { + queryStringParams = await _mediaProfileService.GetMediaProfileCommands(ImageProfile); + } - if (_mediaOptions.UseTokenizedQueryString) - { - resizedSrc = _mediaTokenService.AddTokenToPath(resizedSrc); - } + var resizedSrc = ImageSharpUrlFormatter.GetImageResizeUrl(imgSrc, queryStringParams, ImageWidth, ImageHeight, ResizeMode, ImageQuality, ImageFormat, ImageAnchor, ImageBackgroundColor); - output.Attributes.SetAttribute("src", resizedSrc); + if (_mediaOptions.UseTokenizedQueryString) + { + resizedSrc = _mediaTokenService.AddTokenToPath(resizedSrc); } + + output.Attributes.SetAttribute("src", resizedSrc); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageTagHelper.cs index f57ef8c0ae7..8cac68a6263 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/TagHelpers/ImageTagHelper.cs @@ -2,67 +2,66 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; -namespace OrchardCore.Media.TagHelpers +namespace OrchardCore.Media.TagHelpers; + +[HtmlTargetElement(ImageTagName, Attributes = AssetSrcAttributeName)] +public class ImageTagHelper : TagHelper { - [HtmlTargetElement(ImageTagName, Attributes = AssetSrcAttributeName)] - public class ImageTagHelper : TagHelper - { - private const string AssetSrcAttributeName = "asset-src"; + private const string AssetSrcAttributeName = "asset-src"; - private const string ImageTagName = "img"; + private const string ImageTagName = "img"; - private const string SourceAttributeName = "src"; + private const string SourceAttributeName = "src"; - private const string AppendVersionAttributeName = "asp-append-version"; + private const string AppendVersionAttributeName = "asp-append-version"; - private readonly IMediaFileStore _mediaFileStore; + private readonly IMediaFileStore _mediaFileStore; - private readonly IFileVersionProvider _fileVersionProvider; + private readonly IFileVersionProvider _fileVersionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; - public override int Order => -10; + public override int Order => -10; - [HtmlAttributeName(AssetSrcAttributeName)] - public string AssetSrc { get; set; } + [HtmlAttributeName(AssetSrcAttributeName)] + public string AssetSrc { get; set; } - /// - /// Value indicating if file version should be appended to the src url. - /// - /// - /// If true then a query string "v" with the encoded content of the file is added. - /// - [HtmlAttributeName(AppendVersionAttributeName)] - public bool AppendVersion { get; set; } + /// + /// Value indicating if file version should be appended to the src url. + /// + /// + /// If true then a query string "v" with the encoded content of the file is added. + /// + [HtmlAttributeName(AppendVersionAttributeName)] + public bool AppendVersion { get; set; } - public ImageTagHelper( - IMediaFileStore mediaFileStore, - IHttpContextAccessor httpContextAccessor, - IFileVersionProvider fileVersionProvider - ) - { - _mediaFileStore = mediaFileStore; - _httpContextAccessor = httpContextAccessor; - _fileVersionProvider = fileVersionProvider; - } + public ImageTagHelper( + IMediaFileStore mediaFileStore, + IHttpContextAccessor httpContextAccessor, + IFileVersionProvider fileVersionProvider + ) + { + _mediaFileStore = mediaFileStore; + _httpContextAccessor = httpContextAccessor; + _fileVersionProvider = fileVersionProvider; + } - public override void Process(TagHelperContext context, TagHelperOutput output) + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (string.IsNullOrEmpty(AssetSrc)) { - if (string.IsNullOrEmpty(AssetSrc)) - { - return; - } + return; + } - var resolvedUrl = _mediaFileStore != null ? _mediaFileStore.MapPathToPublicUrl(AssetSrc) : AssetSrc; + var resolvedUrl = _mediaFileStore != null ? _mediaFileStore.MapPathToPublicUrl(AssetSrc) : AssetSrc; - if (AppendVersion && _fileVersionProvider != null) - { - output.Attributes.SetAttribute(SourceAttributeName, _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl)); - } - else - { - output.Attributes.SetAttribute(SourceAttributeName, resolvedUrl); - } + if (AppendVersion && _fileVersionProvider != null) + { + output.Attributes.SetAttribute(SourceAttributeName, _fileVersionProvider.AddFileVersionToPath(_httpContextAccessor.HttpContext.Request.PathBase, resolvedUrl)); + } + else + { + output.Attributes.SetAttribute(SourceAttributeName, resolvedUrl); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/DisplayMediaFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/DisplayMediaFieldViewModel.cs index 2c5a67aa97a..12be9e53743 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/DisplayMediaFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/DisplayMediaFieldViewModel.cs @@ -2,13 +2,12 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Media.Fields; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class DisplayMediaFieldViewModel { - public class DisplayMediaFieldViewModel - { - public string[] Paths => Field.Paths; - public MediaField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string[] Paths => Field.Paths; + public MediaField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/EditMediaFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/EditMediaFieldViewModel.cs index 037ada89a93..0e844693a85 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/EditMediaFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/EditMediaFieldViewModel.cs @@ -2,50 +2,49 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Media.Fields; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class EditMediaFieldViewModel { - public class EditMediaFieldViewModel - { - // A Json serialized array of media paths - public string Paths { get; set; } + // A Json serialized array of media paths + public string Paths { get; set; } - public MediaField Field { get; set; } + public MediaField Field { get; set; } - public ContentPart Part { get; set; } + public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } - // This will be used by the uploader of an attached media field - public string TempUploadFolder { get; set; } + // This will be used by the uploader of an attached media field + public string TempUploadFolder { get; set; } - // Media Text is an option that maybe applied to a media field through settings. - public bool AllowMediaText { get; set; } + // Media Text is an option that maybe applied to a media field through settings. + public bool AllowMediaText { get; set; } - public string MediaTexts { get; set; } + public string MediaTexts { get; set; } - // Anchor points are an option that maybe applied to a media field through settings. - public bool AllowAnchors { get; set; } + // Anchor points are an option that maybe applied to a media field through settings. + public bool AllowAnchors { get; set; } - public Anchor[] Anchors { get; set; } = []; + public Anchor[] Anchors { get; set; } = []; - public string[] AttachedFileNames { get; set; } = []; + public string[] AttachedFileNames { get; set; } = []; - public string[] AllowedExtensions { get; set; } = []; - } + public string[] AllowedExtensions { get; set; } = []; +} - public class EditMediaFieldItemInfo - { - public string Path { get; set; } - public string AttachedFileName { get; set; } +public class EditMediaFieldItemInfo +{ + public string Path { get; set; } + public string AttachedFileName { get; set; } - // It will be true if the media item is a new upload from a attached media field. - public bool IsNew { get; set; } + // It will be true if the media item is a new upload from a attached media field. + public bool IsNew { get; set; } - // It will be true if the media item has been marked for deletion using a attached media field. - public bool IsRemoved { get; set; } + // It will be true if the media item has been marked for deletion using a attached media field. + public bool IsRemoved { get; set; } - // Alt text is an option that maybe applied to a media field through settings. - public string MediaText { get; set; } = string.Empty; - public Anchor Anchor { get; set; } = new Anchor(); - } + // Alt text is an option that maybe applied to a media field through settings. + public string MediaText { get; set; } = string.Empty; + public Anchor Anchor { get; set; } = new Anchor(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaCacheViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaCacheViewModel.cs index d8c4b2265d9..897552ca0b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaCacheViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaCacheViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class MediaCacheViewModel { - public class MediaCacheViewModel - { - public bool IsConfigured { get; set; } - } + public bool IsConfigured { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaDeploymentStepViewModel.cs index 7477821f87a..70549f23345 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaDeploymentStepViewModel.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class MediaDeploymentStepViewModel { - public class MediaDeploymentStepViewModel - { - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] FilePaths { get; set; } + public string[] FilePaths { get; set; } - public string[] DirectoryPaths { get; set; } + public string[] DirectoryPaths { get; set; } - public MediaStoreEntryViewModel ParentEntry { get; set; } + public MediaStoreEntryViewModel ParentEntry { get; set; } - public IList Entries { get; set; } - } + public IList Entries { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileIndexViewModel.cs index 1c36a7e1345..28c1ff615df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileIndexViewModel.cs @@ -3,38 +3,37 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Media.Models; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class MediaProfileIndexViewModel +{ + public IList MediaProfiles { get; set; } + public dynamic Pager { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); +} + +public class MediaProfileEntry +{ + public string Name { get; set; } + public MediaProfile MediaProfile { get; set; } + public bool IsChecked { get; set; } +} + +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + + #endregion Lists to populate +} + +public enum ContentsBulkAction { - public class MediaProfileIndexViewModel - { - public IList MediaProfiles { get; set; } - public dynamic Pager { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - } - - public class MediaProfileEntry - { - public string Name { get; set; } - public MediaProfile MediaProfile { get; set; } - public bool IsChecked { get; set; } - } - - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } - - #region Lists to populate - - [BindNever] - public List ContentsBulkAction { get; set; } - - #endregion Lists to populate - } - - public enum ContentsBulkAction - { - None, - Remove - } + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileViewModel.cs index 6f4be6fef2a..1857e36290a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaProfileViewModel.cs @@ -3,31 +3,30 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Media.Processing; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class MediaProfileViewModel { - public class MediaProfileViewModel - { - public string Name { get; set; } - public string Hint { get; set; } + public string Name { get; set; } + public string Hint { get; set; } - public int SelectedWidth { get; set; } - public int CustomWidth { get; set; } - public int SelectedHeight { get; set; } - public int CustomHeight { get; set; } - public ResizeMode SelectedMode { get; set; } - public Format SelectedFormat { get; set; } - public int Quality { get; set; } = 100; - public string BackgroundColor { get; set; } + public int SelectedWidth { get; set; } + public int CustomWidth { get; set; } + public int SelectedHeight { get; set; } + public int CustomHeight { get; set; } + public ResizeMode SelectedMode { get; set; } + public Format SelectedFormat { get; set; } + public int Quality { get; set; } = 100; + public string BackgroundColor { get; set; } - [BindNever] - public List AvailableWidths { get; set; } = []; + [BindNever] + public List AvailableWidths { get; set; } = []; - [BindNever] - public List AvailableHeights { get; set; } = []; + [BindNever] + public List AvailableHeights { get; set; } = []; - [BindNever] - public List AvailableResizeModes { get; set; } = []; - [BindNever] - public List AvailableFormats { get; set; } = []; - } + [BindNever] + public List AvailableResizeModes { get; set; } = []; + [BindNever] + public List AvailableFormats { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaStoreEntryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaStoreEntryViewModel.cs index ee9283500ad..28004091a6f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaStoreEntryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media/ViewModels/MediaStoreEntryViewModel.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; -namespace OrchardCore.Media.ViewModels +namespace OrchardCore.Media.ViewModels; + +public class MediaStoreEntryViewModel { - public class MediaStoreEntryViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public string Path { get; set; } + public string Path { get; set; } - public MediaStoreEntryViewModel Parent { get; set; } + public MediaStoreEntryViewModel Parent { get; set; } - public IList Entries { get; set; } - } + public IList Entries { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Menu/AdminMenu.cs index 98eb8b0ac7e..3078fef3b94 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/AdminMenu.cs @@ -3,42 +3,41 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Menu +namespace OrchardCore.Menu; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "contentTypeId", "Menu" }, - { "Area", "OrchardCore.Contents" }, - { "Options.SelectedContentType", "Menu" }, - { "Options.CanCreateSelectedContentType", true } - }; + { "contentTypeId", "Menu" }, + { "Area", "OrchardCore.Contents" }, + { "Options.SelectedContentType", "Menu" }, + { "Options.CanCreateSelectedContentType", true } + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Content"], design => design - .Add(S["Menus"], S["Menus"].PrefixPosition(), menus => menus - .Permission(Permissions.ManageMenu) - .Action("List", "Admin", _routeValues) - .LocalNav() - ) - ); - return Task.CompletedTask; } + + builder + .Add(S["Content"], design => design + .Add(S["Menus"], S["Menus"].PrefixPosition(), menus => menus + .Permission(Permissions.ManageMenu) + .Action("List", "Admin", _routeValues) + .LocalNav() + ) + ); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Controllers/AdminController.cs index a624d60a69a..9a76abf13a3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Controllers/AdminController.cs @@ -15,300 +15,299 @@ using OrchardCore.Menu.Models; using YesSql; -namespace OrchardCore.Menu.Controllers +namespace OrchardCore.Menu.Controllers; + +[Admin("Menu/{action}/{id?}", "Menu{action}")] +public class AdminController : Controller { - [Admin("Menu/{action}/{id?}", "Menu{action}")] - public class AdminController : Controller + private readonly IContentManager _contentManager; + private readonly IAuthorizationService _authorizationService; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ISession _session; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + private readonly IUpdateModelAccessor _updateModelAccessor; + + public AdminController( + ISession session, + IContentManager contentManager, + IAuthorizationService authorizationService, + IContentItemDisplayManager contentItemDisplayManager, + IContentDefinitionManager contentDefinitionManager, + INotifier notifier, + IHtmlLocalizer localizer, + IUpdateModelAccessor updateModelAccessor) { - private readonly IContentManager _contentManager; - private readonly IAuthorizationService _authorizationService; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ISession _session; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; - private readonly IUpdateModelAccessor _updateModelAccessor; - - public AdminController( - ISession session, - IContentManager contentManager, - IAuthorizationService authorizationService, - IContentItemDisplayManager contentItemDisplayManager, - IContentDefinitionManager contentDefinitionManager, - INotifier notifier, - IHtmlLocalizer localizer, - IUpdateModelAccessor updateModelAccessor) + _contentManager = contentManager; + _authorizationService = authorizationService; + _contentItemDisplayManager = contentItemDisplayManager; + _contentDefinitionManager = contentDefinitionManager; + _session = session; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + H = localizer; + } + + public async Task Create(string id, string menuContentItemId, string menuItemId) + { + if (string.IsNullOrWhiteSpace(id)) { - _contentManager = contentManager; - _authorizationService = authorizationService; - _contentItemDisplayManager = contentItemDisplayManager; - _contentDefinitionManager = contentDefinitionManager; - _session = session; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - H = localizer; + return NotFound(); } - public async Task Create(string id, string menuContentItemId, string menuItemId) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) { - if (string.IsNullOrWhiteSpace(id)) - { - return NotFound(); - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) - { - return Forbid(); - } + var contentItem = await _contentManager.NewAsync(id); - var contentItem = await _contentManager.NewAsync(id); + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); + model.Properties["MenuContentItemId"] = menuContentItemId; + model.Properties["MenuItemId"] = menuItemId; - model.Properties["MenuContentItemId"] = menuContentItemId; - model.Properties["MenuItemId"] = menuItemId; + return View(model); + } - return View(model); + [HttpPost] + [ActionName("Create")] + public async Task CreatePost(string id, string menuContentItemId, string menuItemId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) + { + return Forbid(); } - [HttpPost] - [ActionName("Create")] - public async Task CreatePost(string id, string menuContentItemId, string menuItemId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) - { - return Forbid(); - } + ContentItem menu; - ContentItem menu; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); + if (!contentTypeDefinition.IsDraftable()) + { + menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); + } + else + { + menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired); + } - if (!contentTypeDefinition.IsDraftable()) - { - menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); - } - else - { - menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired); - } + if (menu == null) + { + return NotFound(); + } - if (menu == null) - { - return NotFound(); - } + var contentItem = await _contentManager.NewAsync(id); - var contentItem = await _contentManager.NewAsync(id); + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); + if (!ModelState.IsValid) + { + model.Properties["MenuContentItemId"] = menuContentItemId; + model.Properties["MenuItemId"] = menuItemId; - if (!ModelState.IsValid) - { - model.Properties["MenuContentItemId"] = menuContentItemId; - model.Properties["MenuItemId"] = menuItemId; + return View(model); + } - return View(model); - } + if (menuItemId == null) + { + // Use the menu as the parent if no target is specified. + menu.Alter(part => part.MenuItems.Add(contentItem)); + } + else + { + // Look for the target menu item in the hierarchy. + var parentMenuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); - if (menuItemId == null) + // Couldn't find targeted menu item. + if (parentMenuItem == null) { - // Use the menu as the parent if no target is specified. - menu.Alter(part => part.MenuItems.Add(contentItem)); + return NotFound(); } - else - { - // Look for the target menu item in the hierarchy. - var parentMenuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); - // Couldn't find targeted menu item. - if (parentMenuItem == null) + var menuItems = (JsonArray)parentMenuItem["MenuItemsListPart"]?["MenuItems"]; + + if (menuItems == null) + { + parentMenuItem["MenuItemsListPart"] = new JsonObject { - return NotFound(); - } + ["MenuItems"] = menuItems = [], + }; + } - var menuItems = (JsonArray)parentMenuItem["MenuItemsListPart"]?["MenuItems"]; + menuItems.Add(JObject.FromObject(contentItem)); + } - if (menuItems == null) - { - parentMenuItem["MenuItemsListPart"] = new JsonObject - { - ["MenuItems"] = menuItems = [], - }; - } + await _contentManager.SaveDraftAsync(menu); - menuItems.Add(JObject.FromObject(contentItem)); - } + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + } - await _contentManager.SaveDraftAsync(menu); + public async Task Edit(string menuContentItemId, string menuItemId) + { + var menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + if (menu == null) + { + return NotFound(); } - public async Task Edit(string menuContentItemId, string menuItemId) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu, menu)) { - var menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); - - if (menu == null) - { - return NotFound(); - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu, menu)) - { - return Forbid(); - } + // Look for the target menu item in the hierarchy. + var menuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); - // Look for the target menu item in the hierarchy. - var menuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); + // Couldn't find targeted menu item. + if (menuItem == null) + { + return NotFound(); + } - // Couldn't find targeted menu item. - if (menuItem == null) - { - return NotFound(); - } + var contentItem = menuItem.ToObject(); - var contentItem = menuItem.ToObject(); + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + model.Properties["MenuContentItemId"] = menuContentItemId; + model.Properties["MenuItemId"] = menuItemId; - model.Properties["MenuContentItemId"] = menuContentItemId; - model.Properties["MenuItemId"] = menuItemId; + return View(model); + } - return View(model); + [HttpPost] + [ActionName("Edit")] + public async Task EditPost(string menuContentItemId, string menuItemId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) + { + return Forbid(); } - [HttpPost] - [ActionName("Edit")] - public async Task EditPost(string menuContentItemId, string menuItemId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) - { - return Forbid(); - } + ContentItem menu; - ContentItem menu; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); + if (!contentTypeDefinition.IsDraftable()) + { + menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); + } + else + { + menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired); + } - if (!contentTypeDefinition.IsDraftable()) - { - menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); - } - else - { - menu = await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired); - } + if (menu == null) + { + return NotFound(); + } - if (menu == null) - { - return NotFound(); - } + // Look for the target menu item in the hierarchy. + var menuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); - // Look for the target menu item in the hierarchy. - var menuItem = FindMenuItem((JsonObject)menu.Content, menuItemId); + // Couldn't find targeted menu item + if (menuItem == null) + { + return NotFound(); + } - // Couldn't find targeted menu item - if (menuItem == null) - { - return NotFound(); - } + var existing = menuItem.ToObject(); - var existing = menuItem.ToObject(); + // Create a new item to take into account the current type definition. + var contentItem = await _contentManager.NewAsync(existing.ContentType); - // Create a new item to take into account the current type definition. - var contentItem = await _contentManager.NewAsync(existing.ContentType); + contentItem.Merge(existing); - contentItem.Merge(existing); + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + if (!ModelState.IsValid) + { + model.Properties["MenuContentItemId"] = menuContentItemId; + model.Properties["MenuItemId"] = menuItemId; - if (!ModelState.IsValid) - { - model.Properties["MenuContentItemId"] = menuContentItemId; - model.Properties["MenuItemId"] = menuItemId; + return View(model); + } - return View(model); - } + menuItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings + { + MergeArrayHandling = MergeArrayHandling.Replace, + MergeNullValueHandling = MergeNullValueHandling.Merge + }); - menuItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings - { - MergeArrayHandling = MergeArrayHandling.Replace, - MergeNullValueHandling = MergeNullValueHandling.Merge - }); + // Merge doesn't copy the properties. + menuItem[nameof(ContentItem.DisplayText)] = contentItem.DisplayText; - // Merge doesn't copy the properties. - menuItem[nameof(ContentItem.DisplayText)] = contentItem.DisplayText; + await _contentManager.SaveDraftAsync(menu); - await _contentManager.SaveDraftAsync(menu); + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + } - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + [HttpPost] + public async Task Delete(string menuContentItemId, string menuItemId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string menuContentItemId, string menuItemId) + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); + var menu = contentTypeDefinition.IsDraftable() + ? await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired) + : await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); + + if (menu == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageMenu)) - { - return Forbid(); - } + return NotFound(); + } - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Menu"); - var menu = contentTypeDefinition.IsDraftable() - ? await _contentManager.GetAsync(menuContentItemId, VersionOptions.DraftRequired) - : await _contentManager.GetAsync(menuContentItemId, VersionOptions.Latest); + var menuContentAsJson = (JsonObject)menu.Content; + // Look for the target menu item in the hierarchy. + var menuItem = FindMenuItem(menuContentAsJson, menuItemId); - if (menu == null) - { - return NotFound(); - } - - var menuContentAsJson = (JsonObject)menu.Content; - // Look for the target menu item in the hierarchy. - var menuItem = FindMenuItem(menuContentAsJson, menuItemId); + // Couldn't find targeted menu item. + if (menuItem == null) + { + return NotFound(); + } - // Couldn't find targeted menu item. - if (menuItem == null) - { - return NotFound(); - } + var menuItems = menuContentAsJson[nameof(MenuItemsListPart)]?[nameof(MenuItemsListPart.MenuItems)] as JsonArray; + menuItems?.Remove(menuItem); - var menuItems = menuContentAsJson[nameof(MenuItemsListPart)]?[nameof(MenuItemsListPart.MenuItems)] as JsonArray; - menuItems?.Remove(menuItem); + await _contentManager.SaveDraftAsync(menu); - await _contentManager.SaveDraftAsync(menu); + await _notifier.SuccessAsync(H["Menu item deleted successfully."]); - await _notifier.SuccessAsync(H["Menu item deleted successfully."]); + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + } - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = menuContentItemId }); + private static JsonObject FindMenuItem(JsonObject contentItem, string menuItemId) + { + if (contentItem["ContentItemId"]?.Value() == menuItemId) + { + return contentItem; } - private static JsonObject FindMenuItem(JsonObject contentItem, string menuItemId) + if (contentItem["MenuItemsListPart"] is null) { - if (contentItem["ContentItemId"]?.Value() == menuItemId) - { - return contentItem; - } + return null; + } - if (contentItem["MenuItemsListPart"] is null) - { - return null; - } + var menuItems = (JsonArray)contentItem["MenuItemsListPart"]["MenuItems"]; - var menuItems = (JsonArray)contentItem["MenuItemsListPart"]["MenuItems"]; + JsonObject result; + foreach (var menuItem in menuItems.Cast()) + { + // Search in inner menu items. + result = FindMenuItem(menuItem, menuItemId); - JsonObject result; - foreach (var menuItem in menuItems.Cast()) + if (result != null) { - // Search in inner menu items. - result = FindMenuItem(menuItem, menuItemId); - - if (result != null) - { - return result; - } + return result; } - - return null; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/ContentMenuItemPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/ContentMenuItemPartDisplayDriver.cs index 1feada74a4e..793e88619f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/ContentMenuItemPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/ContentMenuItemPartDisplayDriver.cs @@ -6,44 +6,43 @@ using OrchardCore.Menu.Models; using OrchardCore.Menu.ViewModels; -namespace OrchardCore.Menu.Drivers +namespace OrchardCore.Menu.Drivers; + +public sealed class ContentMenuItemPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ContentMenuItemPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Display(ContentMenuItemPart part, BuildPartDisplayContext context) { - public override IDisplayResult Display(ContentMenuItemPart part, BuildPartDisplayContext context) - { - return Combine( - Dynamic("ContentMenuItemPart_Admin", shape => - { - shape.MenuItemPart = part; - }) - .Location("Admin", "Content:10"), - Dynamic("ContentMenuItemPart_Thumbnail", shape => - { - shape.MenuItemPart = part; - }) - .Location("Thumbnail", "Content:10") - ); - } - - public override IDisplayResult Edit(ContentMenuItemPart part, BuildPartEditorContext context) - { - return Initialize("ContentMenuItemPart_Edit", model => + return Combine( + Dynamic("ContentMenuItemPart_Admin", shape => + { + shape.MenuItemPart = part; + }) + .Location("Admin", "Content:10"), + Dynamic("ContentMenuItemPart_Thumbnail", shape => { - model.Name = part.ContentItem.DisplayText; - model.MenuItemPart = part; - }); - } + shape.MenuItemPart = part; + }) + .Location("Thumbnail", "Content:10") + ); + } - public override async Task UpdateAsync(ContentMenuItemPart part, UpdatePartEditorContext context) + public override IDisplayResult Edit(ContentMenuItemPart part, BuildPartEditorContext context) + { + return Initialize("ContentMenuItemPart_Edit", model => { - var model = new ContentMenuItemPartEditViewModel(); + model.Name = part.ContentItem.DisplayText; + model.MenuItemPart = part; + }); + } + + public override async Task UpdateAsync(ContentMenuItemPart part, UpdatePartEditorContext context) + { + var model = new ContentMenuItemPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - part.ContentItem.DisplayText = model.Name; + part.ContentItem.DisplayText = model.Name; - return Edit(part, context); - } + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs index 0a72c3800f0..db2c4909079 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs @@ -13,108 +13,107 @@ using OrchardCore.Menu.Settings; using OrchardCore.Menu.ViewModels; -namespace OrchardCore.Menu.Drivers +namespace OrchardCore.Menu.Drivers; + +public sealed class HtmlMenuItemPartDisplayDriver : ContentPartDisplayDriver { - public sealed class HtmlMenuItemPartDisplayDriver : ContentPartDisplayDriver + private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly HtmlEncoder _htmlencoder; + + internal readonly IStringLocalizer S; + + public HtmlMenuItemPartDisplayDriver( + IUrlHelperFactory urlHelperFactory, + IActionContextAccessor actionContextAccessor, + IStringLocalizer localizer, + IHtmlSanitizerService htmlSanitizerService, + HtmlEncoder htmlencoder + ) { - private readonly IUrlHelperFactory _urlHelperFactory; - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly HtmlEncoder _htmlencoder; - - internal readonly IStringLocalizer S; - - public HtmlMenuItemPartDisplayDriver( - IUrlHelperFactory urlHelperFactory, - IActionContextAccessor actionContextAccessor, - IStringLocalizer localizer, - IHtmlSanitizerService htmlSanitizerService, - HtmlEncoder htmlencoder - ) - { - _urlHelperFactory = urlHelperFactory; - _actionContextAccessor = actionContextAccessor; - _htmlSanitizerService = htmlSanitizerService; - _htmlencoder = htmlencoder; - S = localizer; - } - - public override IDisplayResult Display(HtmlMenuItemPart part, BuildPartDisplayContext context) - { - var settings = context.TypePartDefinition.GetSettings(); + _urlHelperFactory = urlHelperFactory; + _actionContextAccessor = actionContextAccessor; + _htmlSanitizerService = htmlSanitizerService; + _htmlencoder = htmlencoder; + S = localizer; + } - if (settings.SanitizeHtml) - { - part.Html = _htmlSanitizerService.Sanitize(part.Html); - } + public override IDisplayResult Display(HtmlMenuItemPart part, BuildPartDisplayContext context) + { + var settings = context.TypePartDefinition.GetSettings(); - return Combine( - Dynamic("HtmlMenuItemPart_Admin", shape => - { - shape.MenuItemPart = part; - }) - .Location("Admin", "Content:10"), - Dynamic("HtmlMenuItemPart_Thumbnail", shape => - { - shape.MenuItemPart = part; - }) - .Location("Thumbnail", "Content:10") - ); + if (settings.SanitizeHtml) + { + part.Html = _htmlSanitizerService.Sanitize(part.Html); } - public override IDisplayResult Edit(HtmlMenuItemPart part, BuildPartEditorContext context) - { - return Initialize("HtmlMenuItemPart_Edit", model => + return Combine( + Dynamic("HtmlMenuItemPart_Admin", shape => { - model.Name = part.ContentItem.DisplayText; - model.Url = part.Url; - model.Target = part.Target; - model.Html = part.Html; - model.MenuItemPart = part; - }); - } + shape.MenuItemPart = part; + }) + .Location("Admin", "Content:10"), + Dynamic("HtmlMenuItemPart_Thumbnail", shape => + { + shape.MenuItemPart = part; + }) + .Location("Thumbnail", "Content:10") + ); + } - public override async Task UpdateAsync(HtmlMenuItemPart part, UpdatePartEditorContext context) + public override IDisplayResult Edit(HtmlMenuItemPart part, BuildPartEditorContext context) + { + return Initialize("HtmlMenuItemPart_Edit", model => { - var settings = context.TypePartDefinition.GetSettings(); - var model = new HtmlMenuItemPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + model.Name = part.ContentItem.DisplayText; + model.Url = part.Url; + model.Target = part.Target; + model.Html = part.Html; + model.MenuItemPart = part; + }); + } + + public override async Task UpdateAsync(HtmlMenuItemPart part, UpdatePartEditorContext context) + { + var settings = context.TypePartDefinition.GetSettings(); + var model = new HtmlMenuItemPartEditViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + + part.ContentItem.DisplayText = model.Name; + part.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(model.Html) : model.Html; + part.Url = model.Url; + part.Target = model.Target; - part.ContentItem.DisplayText = model.Name; - part.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(model.Html) : model.Html; - part.Url = model.Url; - part.Target = model.Target; + var urlToValidate = part.Url; - var urlToValidate = part.Url; + if (!string.IsNullOrEmpty(urlToValidate)) + { + urlToValidate = urlToValidate.Split('#', 2)[0]; - if (!string.IsNullOrEmpty(urlToValidate)) + if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) { - urlToValidate = urlToValidate.Split('#', 2)[0]; + var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); + urlToValidate = urlHelper.Content(urlToValidate); + } - if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) - { - var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); - urlToValidate = urlHelper.Content(urlToValidate); - } + urlToValidate = urlToValidate.ToUriComponents(); - urlToValidate = urlToValidate.ToUriComponents(); + if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) + { + context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); + } + else + { + var link = $""; - if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) + if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) { context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); } - else - { - var link = $""; - - if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) - { - context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); - } - } } - - return Edit(part, context); } + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs index 2e8cb84f694..ca52cc2fb38 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs @@ -12,99 +12,98 @@ using OrchardCore.Menu.Models; using OrchardCore.Menu.ViewModels; -namespace OrchardCore.Menu.Drivers +namespace OrchardCore.Menu.Drivers; + +public sealed class LinkMenuItemPartDisplayDriver : ContentPartDisplayDriver { - public sealed class LinkMenuItemPartDisplayDriver : ContentPartDisplayDriver - { - private readonly IUrlHelperFactory _urlHelperFactory; - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IHtmlSanitizerService _htmlSanitizerService; - private readonly HtmlEncoder _htmlEncoder; - - internal readonly IStringLocalizer S; - - public LinkMenuItemPartDisplayDriver( - IUrlHelperFactory urlHelperFactory, - IActionContextAccessor actionContextAccessor, - IStringLocalizer localizer, - IHtmlSanitizerService htmlSanitizerService, - HtmlEncoder htmlEncoder - ) - { - _urlHelperFactory = urlHelperFactory; - _actionContextAccessor = actionContextAccessor; - _htmlSanitizerService = htmlSanitizerService; - _htmlEncoder = htmlEncoder; - S = localizer; - } + private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IHtmlSanitizerService _htmlSanitizerService; + private readonly HtmlEncoder _htmlEncoder; - public override IDisplayResult Display(LinkMenuItemPart part, BuildPartDisplayContext context) - { - return Combine( - Dynamic("LinkMenuItemPart_Admin", shape => - { - shape.MenuItemPart = part; - }) - .Location("Admin", "Content:10"), - Dynamic("LinkMenuItemPart_Thumbnail", shape => - { - shape.MenuItemPart = part; - }) - .Location("Thumbnail", "Content:10") - ); - } + internal readonly IStringLocalizer S; - public override IDisplayResult Edit(LinkMenuItemPart part, BuildPartEditorContext context) - { - return Initialize("LinkMenuItemPart_Edit", model => + public LinkMenuItemPartDisplayDriver( + IUrlHelperFactory urlHelperFactory, + IActionContextAccessor actionContextAccessor, + IStringLocalizer localizer, + IHtmlSanitizerService htmlSanitizerService, + HtmlEncoder htmlEncoder + ) + { + _urlHelperFactory = urlHelperFactory; + _actionContextAccessor = actionContextAccessor; + _htmlSanitizerService = htmlSanitizerService; + _htmlEncoder = htmlEncoder; + S = localizer; + } + + public override IDisplayResult Display(LinkMenuItemPart part, BuildPartDisplayContext context) + { + return Combine( + Dynamic("LinkMenuItemPart_Admin", shape => { - model.Name = part.ContentItem.DisplayText; - model.Url = part.Url; - model.Target = part.Target; - model.MenuItemPart = part; - }); - } + shape.MenuItemPart = part; + }) + .Location("Admin", "Content:10"), + Dynamic("LinkMenuItemPart_Thumbnail", shape => + { + shape.MenuItemPart = part; + }) + .Location("Thumbnail", "Content:10") + ); + } - public override async Task UpdateAsync(LinkMenuItemPart part, UpdatePartEditorContext context) + public override IDisplayResult Edit(LinkMenuItemPart part, BuildPartEditorContext context) + { + return Initialize("LinkMenuItemPart_Edit", model => { - var model = new LinkMenuItemPartEditViewModel(); + model.Name = part.ContentItem.DisplayText; + model.Url = part.Url; + model.Target = part.Target; + model.MenuItemPart = part; + }); + } + + public override async Task UpdateAsync(LinkMenuItemPart part, UpdatePartEditorContext context) + { + var model = new LinkMenuItemPartEditViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix); - await context.Updater.TryUpdateModelAsync(model, Prefix); + part.Url = model.Url; + part.Target = model.Target; + part.ContentItem.DisplayText = model.Name; - part.Url = model.Url; - part.Target = model.Target; - part.ContentItem.DisplayText = model.Name; + var urlToValidate = part.Url; - var urlToValidate = part.Url; + if (!string.IsNullOrEmpty(urlToValidate)) + { + urlToValidate = urlToValidate.Split('#', 2)[0]; - if (!string.IsNullOrEmpty(urlToValidate)) + if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) { - urlToValidate = urlToValidate.Split('#', 2)[0]; + var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); + urlToValidate = urlHelper.Content(urlToValidate); + } - if (urlToValidate.StartsWith("~/", StringComparison.Ordinal)) - { - var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); - urlToValidate = urlHelper.Content(urlToValidate); - } + urlToValidate = urlToValidate.ToUriComponents(); - urlToValidate = urlToValidate.ToUriComponents(); + if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) + { + context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); + } + else + { + var link = $""; - if (!Uri.IsWellFormedUriString(urlToValidate, UriKind.RelativeOrAbsolute)) + if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) { context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); } - else - { - var link = $""; - - if (!string.Equals(link, _htmlSanitizerService.Sanitize(link), StringComparison.OrdinalIgnoreCase)) - { - context.Updater.ModelState.AddModelError(nameof(part.Url), S["{0} is an invalid url.", part.Url]); - } - } } - - return Edit(part, context); } + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/MenuPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/MenuPartDisplayDriver.cs index c7f31e83f1f..87c02fcacde 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/MenuPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/MenuPartDisplayDriver.cs @@ -16,139 +16,138 @@ using OrchardCore.Menu.Models; using OrchardCore.Menu.ViewModels; -namespace OrchardCore.Menu.Drivers +namespace OrchardCore.Menu.Drivers; + +public sealed class MenuPartDisplayDriver : ContentPartDisplayDriver { - public sealed class MenuPartDisplayDriver : ContentPartDisplayDriver + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + internal readonly IHtmlLocalizer H; + + public MenuPartDisplayDriver( + IContentDefinitionManager contentDefinitionManager, + INotifier notifier, + ILogger logger, + IHtmlLocalizer htmlLocalizer + ) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - internal readonly IHtmlLocalizer H; - - public MenuPartDisplayDriver( - IContentDefinitionManager contentDefinitionManager, - INotifier notifier, - ILogger logger, - IHtmlLocalizer htmlLocalizer - ) - { - _contentDefinitionManager = contentDefinitionManager; - _notifier = notifier; - _logger = logger; - H = htmlLocalizer; - } + _contentDefinitionManager = contentDefinitionManager; + _notifier = notifier; + _logger = logger; + H = htmlLocalizer; + } - public override IDisplayResult Edit(MenuPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(MenuPart part, BuildPartEditorContext context) + { + return Initialize("MenuPart_Edit", async model => { - return Initialize("MenuPart_Edit", async model => - { - var menuItemContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Where(t => t.StereotypeEquals("MenuItem")) - .ToArray(); + var menuItemContentTypes = (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Where(t => t.StereotypeEquals("MenuItem")) + .ToArray(); - var notify = false; + var notify = false; - foreach (var menuItem in part.ContentItem.As().MenuItems) + foreach (var menuItem in part.ContentItem.As().MenuItems) + { + if (!menuItemContentTypes.Any(c => c.Name == menuItem.ContentType)) { - if (!menuItemContentTypes.Any(c => c.Name == menuItem.ContentType)) - { - _logger.LogWarning("The menu item content item with id {ContentItemId} has no matching {ContentType} content type definition.", menuItem.ContentItem.ContentItemId, menuItem.ContentItem.ContentType); - await _notifier.WarningAsync(H["The menu item content item with id {0} has no matching {1} content type definition.", menuItem.ContentItem.ContentItemId, menuItem.ContentItem.ContentType]); - notify = true; - } + _logger.LogWarning("The menu item content item with id {ContentItemId} has no matching {ContentType} content type definition.", menuItem.ContentItem.ContentItemId, menuItem.ContentItem.ContentType); + await _notifier.WarningAsync(H["The menu item content item with id {0} has no matching {1} content type definition.", menuItem.ContentItem.ContentItemId, menuItem.ContentItem.ContentType]); + notify = true; } + } - if (notify) - { - await _notifier.WarningAsync(H["Publishing this content item may erase created content. Fix any content type issues beforehand."]); - } + if (notify) + { + await _notifier.WarningAsync(H["Publishing this content item may erase created content. Fix any content type issues beforehand."]); + } - model.MenuPart = part; - model.MenuItemContentTypes = menuItemContentTypes; - }); - } + model.MenuPart = part; + model.MenuItemContentTypes = menuItemContentTypes; + }); + } + + public override async Task UpdateAsync(MenuPart part, UpdatePartEditorContext context) + { + var model = new MenuPartEditViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Hierarchy); - public override async Task UpdateAsync(MenuPart part, UpdatePartEditorContext context) + if (!string.IsNullOrWhiteSpace(model.Hierarchy)) { - var model = new MenuPartEditViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Hierarchy); + var menuItems = new JsonArray(); - if (!string.IsNullOrWhiteSpace(model.Hierarchy)) - { - var menuItems = new JsonArray(); + var originalMenuItems = part.ContentItem.As(); - var originalMenuItems = part.ContentItem.As(); + if (originalMenuItems is not null) + { + var newHierarchy = JArray.Parse(model.Hierarchy); - if (originalMenuItems is not null) + foreach (var item in newHierarchy) { - var newHierarchy = JArray.Parse(model.Hierarchy); - - foreach (var item in newHierarchy) - { - menuItems.Add(ProcessItem(originalMenuItems, item as JsonObject)); - } + menuItems.Add(ProcessItem(originalMenuItems, item as JsonObject)); } - - part.ContentItem.Content[nameof(MenuItemsListPart)] = new JsonObject - { - [nameof(MenuItemsListPart.MenuItems)] = menuItems, - }; } - return Edit(part, context); + part.ContentItem.Content[nameof(MenuItemsListPart)] = new JsonObject + { + [nameof(MenuItemsListPart.MenuItems)] = menuItems, + }; } - /// - /// Clone the content items at the specific index. - /// - private static JsonObject GetMenuItemAt(MenuItemsListPart menuItems, int[] indexes) - { - ContentItem menuItem = null; + return Edit(part, context); + } - foreach (var index in indexes) - { - menuItem = menuItems.MenuItems[index]; - menuItems = menuItem.As(); - } + /// + /// Clone the content items at the specific index. + /// + private static JsonObject GetMenuItemAt(MenuItemsListPart menuItems, int[] indexes) + { + ContentItem menuItem = null; - var newObj = JObject.FromObject(menuItem, JOptions.Default); + foreach (var index in indexes) + { + menuItem = menuItems.MenuItems[index]; + menuItems = menuItem.As(); + } - if (newObj[nameof(MenuItemsListPart)] != null) - { - newObj[nameof(MenuItemsListPart)] = new JsonObject - { - [nameof(MenuItemsListPart.MenuItems)] = new JsonArray() - }; - } + var newObj = JObject.FromObject(menuItem, JOptions.Default); - return newObj; + if (newObj[nameof(MenuItemsListPart)] != null) + { + newObj[nameof(MenuItemsListPart)] = new JsonObject + { + [nameof(MenuItemsListPart.MenuItems)] = new JsonArray() + }; } - private static JsonObject ProcessItem(MenuItemsListPart originalItems, JsonObject item) - { - var indexes = item["index"]?.ToString().Split('-').Select(x => Convert.ToInt32(x)).ToArray() ?? []; + return newObj; + } - var contentItem = GetMenuItemAt(originalItems, indexes); + private static JsonObject ProcessItem(MenuItemsListPart originalItems, JsonObject item) + { + var indexes = item["index"]?.ToString().Split('-').Select(x => Convert.ToInt32(x)).ToArray() ?? []; - var children = item["children"] as JsonArray; + var contentItem = GetMenuItemAt(originalItems, indexes); - if (children is not null) - { - var menuItems = new JsonArray(); + var children = item["children"] as JsonArray; - for (var i = 0; i < children.Count; i++) - { - menuItems.Add(ProcessItem(originalItems, children[i] as JsonObject)); - } + if (children is not null) + { + var menuItems = new JsonArray(); - contentItem[nameof(MenuItemsListPart)] = new JsonObject - { - [nameof(MenuItemsListPart.MenuItems)] = menuItems, - }; + for (var i = 0; i < children.Count; i++) + { + menuItems.Add(ProcessItem(originalItems, children[i] as JsonObject)); } - return contentItem; + contentItem[nameof(MenuItemsListPart)] = new JsonObject + { + [nameof(MenuItemsListPart.MenuItems)] = menuItems, + }; } + + return contentItem; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs index bd57ae30466..c2fbf91727c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs @@ -1,17 +1,16 @@ using GraphQL.Types; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +public class HtmlMenuItemQueryObjectType : ObjectGraphType { - public class HtmlMenuItemQueryObjectType : ObjectGraphType + public HtmlMenuItemQueryObjectType() { - public HtmlMenuItemQueryObjectType() - { - Name = "HtmlMenuItemPart"; + Name = "HtmlMenuItemPart"; - Field(x => x.Url, nullable: true); - Field(x => x.Target, nullable: true); - Field(x => x.Html, nullable: true); - } + Field(x => x.Url, nullable: true); + Field(x => x.Target, nullable: true); + Field(x => x.Html, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/LinkMenuItemQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/LinkMenuItemQueryObjectType.cs index 4c0d9746eae..2f357eae9dc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/LinkMenuItemQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/LinkMenuItemQueryObjectType.cs @@ -1,15 +1,14 @@ using GraphQL.Types; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +public class LinkMenuItemQueryObjectType : ObjectGraphType { - public class LinkMenuItemQueryObjectType : ObjectGraphType + public LinkMenuItemQueryObjectType() { - public LinkMenuItemQueryObjectType() - { - Name = "LinkMenuItemPart"; + Name = "LinkMenuItemPart"; - Field(x => x.Url, nullable: true); - } + Field(x => x.Url, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemContentTypeBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemContentTypeBuilder.cs index f8476e23840..6b1b8858d31 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemContentTypeBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemContentTypeBuilder.cs @@ -6,25 +6,24 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +public class MenuItemContentTypeBuilder : IContentTypeBuilder { - public class MenuItemContentTypeBuilder : IContentTypeBuilder + public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType) { - public void Build(ISchema schema, FieldType contentQuery, ContentTypeDefinition contentTypeDefinition, ContentItemType contentItemType) + if (contentTypeDefinition.GetStereotype() != "MenuItem") { - if (contentTypeDefinition.GetStereotype() != "MenuItem") - { - return; - } + return; + } - contentItemType.AddField(new FieldType - { - Type = typeof(MenuItemsListQueryObjectType), - Name = nameof(MenuItemsListPart).ToFieldName(), - Resolver = new FuncFieldResolver(context => context.Source.As()) - }); + contentItemType.AddField(new FieldType + { + Type = typeof(MenuItemsListQueryObjectType), + Name = nameof(MenuItemsListPart).ToFieldName(), + Resolver = new FuncFieldResolver(context => context.Source.As()) + }); - contentItemType.Interface(); - } + contentItemType.Interface(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemInterface.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemInterface.cs index 9692509eb5b..23ef0cae5cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemInterface.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemInterface.cs @@ -1,14 +1,13 @@ using GraphQL.Types; using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +public class MenuItemInterface : InterfaceGraphType { - public class MenuItemInterface : InterfaceGraphType + public MenuItemInterface() { - public MenuItemInterface() - { - Name = "MenuItem"; - Field("menuItemsList"); - } + Name = "MenuItem"; + Field("menuItemsList"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs index 19ccba88ccc..24308cf57ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/MenuItemsListQueryObjectType.cs @@ -1,18 +1,17 @@ using GraphQL.Types; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +public class MenuItemsListQueryObjectType : ObjectGraphType { - public class MenuItemsListQueryObjectType : ObjectGraphType + public MenuItemsListQueryObjectType() { - public MenuItemsListQueryObjectType() - { - Name = "MenuItemsListPart"; + Name = "MenuItemsListPart"; - Field>("menuItems") - .Description("The menu items.") - .Resolve(context => context.Source.MenuItems); + Field>("menuItems") + .Description("The menu items.") + .Resolve(context => context.Source.MenuItems); - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/Startup.cs index b26bd9dc8aa..9c4efb9e181 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/Startup.cs @@ -4,17 +4,16 @@ using OrchardCore.Menu.Models; using OrchardCore.Modules; -namespace OrchardCore.Menu.GraphQL +namespace OrchardCore.Menu.GraphQL; + +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddObjectGraphType(); - services.AddScoped(); - } + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddObjectGraphType(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Handlers/MenuContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Handlers/MenuContentHandler.cs index 85261db1694..470b91d1178 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Handlers/MenuContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Handlers/MenuContentHandler.cs @@ -3,20 +3,19 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.Handlers +namespace OrchardCore.Menu.Handlers; + +public class MenuContentHandler : ContentHandlerBase { - public class MenuContentHandler : ContentHandlerBase + public override Task ActivatedAsync(ActivatedContentContext context) { - public override Task ActivatedAsync(ActivatedContentContext context) + // When a Menu is created, we add a MenuPart to it + if (context.ContentItem.ContentType == "Menu") { - // When a Menu is created, we add a MenuPart to it - if (context.ContentItem.ContentType == "Menu") - { - context.ContentItem.Weld(new { Position = "3" }); - context.ContentItem.Weld(new { Position = "4" }); - } - - return Task.CompletedTask; + context.ContentItem.Weld(new { Position = "3" }); + context.ContentItem.Weld(new { Position = "4" }); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/MenuShapes.cs b/src/OrchardCore.Modules/OrchardCore.Menu/MenuShapes.cs index f957782ed4a..66e2f518da8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/MenuShapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/MenuShapes.cs @@ -9,211 +9,210 @@ using OrchardCore.Menu.Models; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.Menu +namespace OrchardCore.Menu; + +public class MenuShapes : ShapeTableProvider { - public class MenuShapes : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("Menu") - .OnProcessing(async context => + builder.Describe("Menu") + .OnProcessing(async context => + { + var menu = context.Shape; + + // Menu population is executed when processing the shape so that its value + // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor + // events and thus this code can be cached. + + var shapeFactory = context.ServiceProvider.GetRequiredService(); + var contentManager = context.ServiceProvider.GetRequiredService(); + var handleManager = context.ServiceProvider.GetRequiredService(); + + var contentItemId = menu.TryGetProperty("Alias", out object alias) && alias != null + ? await handleManager.GetContentItemIdAsync(alias.ToString()) + : menu.TryGetProperty("ContentItemId", out object id) + ? id.ToString() + : null; + + if (contentItemId == null) { - var menu = context.Shape; + return; + } - // Menu population is executed when processing the shape so that its value - // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor - // events and thus this code can be cached. + menu.Classes.Add("menu"); - var shapeFactory = context.ServiceProvider.GetRequiredService(); - var contentManager = context.ServiceProvider.GetRequiredService(); - var handleManager = context.ServiceProvider.GetRequiredService(); + var menuContentItem = await contentManager.GetAsync(contentItemId); - var contentItemId = menu.TryGetProperty("Alias", out object alias) && alias != null - ? await handleManager.GetContentItemIdAsync(alias.ToString()) - : menu.TryGetProperty("ContentItemId", out object id) - ? id.ToString() - : null; + if (menuContentItem == null) + { + return; + } - if (contentItemId == null) - { - return; - } + menu.Properties["ContentItem"] = menuContentItem; - menu.Classes.Add("menu"); + menu.Properties["MenuName"] = menuContentItem.DisplayText; - var menuContentItem = await contentManager.GetAsync(contentItemId); + var menuItems = menuContentItem.As()?.MenuItems; - if (menuContentItem == null) - { - return; - } + if (menuItems == null) + { + return; + } - menu.Properties["ContentItem"] = menuContentItem; + var differentiator = FormatName(menu.GetProperty("MenuName")); - menu.Properties["MenuName"] = menuContentItem.DisplayText; + if (!string.IsNullOrEmpty(differentiator)) + { + // Menu__[MenuName] e.g. Menu-MainMenu + menu.Metadata.Alternates.Add("Menu__" + differentiator); + menu.Metadata.Differentiator = differentiator; + menu.Classes.Add(("menu-" + differentiator).HtmlClassify()); + } - var menuItems = menuContentItem.As()?.MenuItems; + // The first level of menu item shapes is created. + // Each other level is created when the menu item is displayed. - if (menuItems == null) + foreach (var contentItem in menuItems) + { + var shape = await shapeFactory.CreateAsync("MenuItem", Arguments.From(new { - return; - } + ContentItem = contentItem, + Level = 0, + Menu = menu + })); - var differentiator = FormatName(menu.GetProperty("MenuName")); + shape.Metadata.Differentiator = differentiator; - if (!string.IsNullOrEmpty(differentiator)) - { - // Menu__[MenuName] e.g. Menu-MainMenu - menu.Metadata.Alternates.Add("Menu__" + differentiator); - menu.Metadata.Differentiator = differentiator; - menu.Classes.Add(("menu-" + differentiator).HtmlClassify()); - } + // Don't use Items.Add() or the collection won't be sorted + await ((Shape)menu).AddAsync(shape); + } + }); - // The first level of menu item shapes is created. - // Each other level is created when the menu item is displayed. + builder.Describe("MenuItem") + .OnDisplaying(async context => + { + var menuItem = context.Shape; + var menuContentItem = menuItem.GetProperty("ContentItem"); + var menu = menuItem.GetProperty("Menu"); + var level = menuItem.GetProperty("Level"); + var differentiator = menuItem.Metadata.Differentiator; + var shapeFactory = context.ServiceProvider.GetRequiredService(); + + var menuItems = menuContentItem.As()?.MenuItems; + + if (menuItems != null) + { foreach (var contentItem in menuItems) { var shape = await shapeFactory.CreateAsync("MenuItem", Arguments.From(new { ContentItem = contentItem, - Level = 0, + Level = level + 1, Menu = menu })); shape.Metadata.Differentiator = differentiator; // Don't use Items.Add() or the collection won't be sorted - await ((Shape)menu).AddAsync(shape); + await menuItem.AddAsync(shape); } - }); - - builder.Describe("MenuItem") - .OnDisplaying(async context => - { - var menuItem = context.Shape; - var menuContentItem = menuItem.GetProperty("ContentItem"); - var menu = menuItem.GetProperty("Menu"); - var level = menuItem.GetProperty("Level"); - var differentiator = menuItem.Metadata.Differentiator; - - var shapeFactory = context.ServiceProvider.GetRequiredService(); - - var menuItems = menuContentItem.As()?.MenuItems; + } - if (menuItems != null) - { - foreach (var contentItem in menuItems) - { - var shape = await shapeFactory.CreateAsync("MenuItem", Arguments.From(new - { - ContentItem = contentItem, - Level = level + 1, - Menu = menu - })); - - shape.Metadata.Differentiator = differentiator; - - // Don't use Items.Add() or the collection won't be sorted - await menuItem.AddAsync(shape); - } - } + var encodedContentType = menuContentItem.ContentItem.ContentType.EncodeAlternateElement(); - var encodedContentType = menuContentItem.ContentItem.ContentType.EncodeAlternateElement(); + // MenuItem__level__[level] e.g. MenuItem-level-2 + menuItem.Metadata.Alternates.Add("MenuItem__level__" + level); - // MenuItem__level__[level] e.g. MenuItem-level-2 - menuItem.Metadata.Alternates.Add("MenuItem__level__" + level); + // MenuItem__[ContentType] e.g. MenuItem-HtmlMenuItem + // MenuItem__[ContentType]__level__[level] e.g. MenuItem-HtmlMenuItem-level-2 + menuItem.Metadata.Alternates.Add("MenuItem__" + encodedContentType); + menuItem.Metadata.Alternates.Add("MenuItem__" + encodedContentType + "__level__" + level); - // MenuItem__[ContentType] e.g. MenuItem-HtmlMenuItem - // MenuItem__[ContentType]__level__[level] e.g. MenuItem-HtmlMenuItem-level-2 - menuItem.Metadata.Alternates.Add("MenuItem__" + encodedContentType); - menuItem.Metadata.Alternates.Add("MenuItem__" + encodedContentType + "__level__" + level); + if (!string.IsNullOrEmpty(differentiator)) + { + // MenuItem__[MenuName] e.g. MenuItem-MainMenu + // MenuItem__[MenuName]__level__[level] e.g. MenuItem-MainMenu-level-2 + menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator); + menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__level__" + level); + + // MenuItem__[MenuName]__[ContentType] e.g. MenuItem-MainMenu-HtmlMenuItem + // MenuItem__[MenuName]__[ContentType]__level__[level] e.g. MenuItem-MainMenu-HtmlMenuItem-level-2 + menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__" + encodedContentType); + menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__" + encodedContentType + "__level__" + level); + } + }); - if (!string.IsNullOrEmpty(differentiator)) - { - // MenuItem__[MenuName] e.g. MenuItem-MainMenu - // MenuItem__[MenuName]__level__[level] e.g. MenuItem-MainMenu-level-2 - menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator); - menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__level__" + level); - - // MenuItem__[MenuName]__[ContentType] e.g. MenuItem-MainMenu-HtmlMenuItem - // MenuItem__[MenuName]__[ContentType]__level__[level] e.g. MenuItem-MainMenu-HtmlMenuItem-level-2 - menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__" + encodedContentType); - menuItem.Metadata.Alternates.Add("MenuItem__" + differentiator + "__" + encodedContentType + "__level__" + level); - } - }); + builder.Describe("MenuItemLink") + .OnDisplaying(displaying => + { + var menuItem = displaying.Shape; + var level = menuItem.GetProperty("Level"); + var differentiator = menuItem.Metadata.Differentiator; - builder.Describe("MenuItemLink") - .OnDisplaying(displaying => - { - var menuItem = displaying.Shape; - var level = menuItem.GetProperty("Level"); - var differentiator = menuItem.Metadata.Differentiator; + var menuContentItem = menuItem.GetProperty("ContentItem"); - var menuContentItem = menuItem.GetProperty("ContentItem"); + var encodedContentType = menuContentItem.ContentItem.ContentType.EncodeAlternateElement(); - var encodedContentType = menuContentItem.ContentItem.ContentType.EncodeAlternateElement(); + menuItem.Metadata.Alternates.Add("MenuItemLink__level__" + level); - menuItem.Metadata.Alternates.Add("MenuItemLink__level__" + level); + // MenuItemLink__[ContentType] e.g. MenuItemLink-HtmlMenuItem + // MenuItemLink__[ContentType]__level__[level] e.g. MenuItemLink-HtmlMenuItem-level-2 + menuItem.Metadata.Alternates.Add("MenuItemLink__" + encodedContentType); + menuItem.Metadata.Alternates.Add("MenuItemLink__" + encodedContentType + "__level__" + level); - // MenuItemLink__[ContentType] e.g. MenuItemLink-HtmlMenuItem - // MenuItemLink__[ContentType]__level__[level] e.g. MenuItemLink-HtmlMenuItem-level-2 - menuItem.Metadata.Alternates.Add("MenuItemLink__" + encodedContentType); - menuItem.Metadata.Alternates.Add("MenuItemLink__" + encodedContentType + "__level__" + level); + if (!string.IsNullOrEmpty(differentiator)) + { + // MenuItemLink__[MenuName] e.g. MenuItemLink-MainMenu + // MenuItemLink__[MenuName]__level__[level] e.g. MenuItemLink-MainMenu-level-2 + menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator); + menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__level__" + level); + + // MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-MainMenu-HtmlMenuItem + // MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-MainMenu-HtmlMenuItem-level-2 + menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__" + encodedContentType); + menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__" + encodedContentType + "__level__" + level); + } + }); - if (!string.IsNullOrEmpty(differentiator)) - { - // MenuItemLink__[MenuName] e.g. MenuItemLink-MainMenu - // MenuItemLink__[MenuName]__level__[level] e.g. MenuItemLink-MainMenu-level-2 - menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator); - menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__level__" + level); - - // MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-MainMenu-HtmlMenuItem - // MenuItemLink__[MenuName]__[ContentType] e.g. MenuItemLink-MainMenu-HtmlMenuItem-level-2 - menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__" + encodedContentType); - menuItem.Metadata.Alternates.Add("MenuItemLink__" + differentiator + "__" + encodedContentType + "__level__" + level); - } - }); + return ValueTask.CompletedTask; + } - return ValueTask.CompletedTask; + /// + /// Converts "foo-ba r" to "FooBaR". + /// + private static string FormatName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return null; } - /// - /// Converts "foo-ba r" to "FooBaR". - /// - private static string FormatName(string name) + name = name.Trim(); + var nextIsUpper = true; + var result = new StringBuilder(name.Length); + for (var i = 0; i < name.Length; i++) { - if (string.IsNullOrEmpty(name)) + var c = name[i]; + + if (c == '-' || char.IsWhiteSpace(c)) { - return null; + nextIsUpper = true; + continue; } - name = name.Trim(); - var nextIsUpper = true; - var result = new StringBuilder(name.Length); - for (var i = 0; i < name.Length; i++) + if (nextIsUpper) { - var c = name[i]; - - if (c == '-' || char.IsWhiteSpace(c)) - { - nextIsUpper = true; - continue; - } - - if (nextIsUpper) - { - result.Append(c.ToString().ToUpper()); - } - else - { - result.Append(c); - } - - nextIsUpper = false; + result.Append(c.ToString().ToUpper()); + } + else + { + result.Append(c); } - return result.ToString(); + nextIsUpper = false; } + + return result.ToString(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Migrations.cs index 9d4b556914d..21d08e0d5dd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Migrations.cs @@ -8,76 +8,75 @@ using OrchardCore.Recipes.Services; using YesSql; -namespace OrchardCore.Menu +namespace OrchardCore.Menu; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IRecipeMigrator _recipeMigrator; + private readonly ISession _session; + + public Migrations( + IRecipeMigrator recipeMigrator, + ISession session) { - private readonly IRecipeMigrator _recipeMigrator; - private readonly ISession _session; + _recipeMigrator = recipeMigrator; + _session = session; + } - public Migrations( - IRecipeMigrator recipeMigrator, - ISession session) - { - _recipeMigrator = recipeMigrator; - _session = session; - } + public async Task CreateAsync() + { + await _recipeMigrator.ExecuteAsync($"menu{RecipesConstants.RecipeExtension}", this); - public async Task CreateAsync() - { - await _recipeMigrator.ExecuteAsync($"menu{RecipesConstants.RecipeExtension}", this); + // Shortcut other migration steps on new content definition schemas. + return 4; + } - // Shortcut other migration steps on new content definition schemas. - return 4; - } + // Add content menu. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom1Async() + { + await _recipeMigrator.ExecuteAsync($"content-menu-updatefrom1{RecipesConstants.RecipeExtension}", this); - // Add content menu. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _recipeMigrator.ExecuteAsync($"content-menu-updatefrom1{RecipesConstants.RecipeExtension}", this); + return 2; + } - return 2; - } + // Add html menu. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom2Async() + { + await _recipeMigrator.ExecuteAsync($"html-menu-updatefrom2{RecipesConstants.RecipeExtension}", this); - // Add html menu. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await _recipeMigrator.ExecuteAsync($"html-menu-updatefrom2{RecipesConstants.RecipeExtension}", this); + return 3; + } - return 3; - } + public async Task UpdateFrom3Async() + { + var menus = await _session.Query(x => x.ContentType == "Menu").ListAsync(); - public async Task UpdateFrom3Async() + foreach (var menu in menus) { - var menus = await _session.Query(x => x.ContentType == "Menu").ListAsync(); - - foreach (var menu in menus) + var menuItemsListPart = menu.As(); + if (menuItemsListPart != null) { - var menuItemsListPart = menu.As(); - if (menuItemsListPart != null) - { - MigrateMenuItems(menuItemsListPart.MenuItems); - menu.Apply(menuItemsListPart); - } - - await _session.SaveAsync(menu); + MigrateMenuItems(menuItemsListPart.MenuItems); + menu.Apply(menuItemsListPart); } - return 4; + await _session.SaveAsync(menu); } - private static void MigrateMenuItems(List menuItems) + return 4; + } + + private static void MigrateMenuItems(List menuItems) + { + foreach (var menuItem in menuItems) { - foreach (var menuItem in menuItems) + var menuItemsListPart = menuItem.As(); + if (menuItemsListPart != null) { - var menuItemsListPart = menuItem.As(); - if (menuItemsListPart != null) - { - MigrateMenuItems(menuItemsListPart.MenuItems); - menuItem.Apply(menuItemsListPart); - } + MigrateMenuItems(menuItemsListPart.MenuItems); + menuItem.Apply(menuItemsListPart); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/ContentMenuItemPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/ContentMenuItemPart.cs index 7506cd9e372..1d4303b56f1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/ContentMenuItemPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/ContentMenuItemPart.cs @@ -1,8 +1,7 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.Models +namespace OrchardCore.Menu.Models; + +public class ContentMenuItemPart : ContentPart { - public class ContentMenuItemPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs index cabd9772546..e96e71f655a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs @@ -1,22 +1,21 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.Models +namespace OrchardCore.Menu.Models; + +public class HtmlMenuItemPart : ContentPart { - public class HtmlMenuItemPart : ContentPart - { - /// - /// The url of the link to create. - /// - public string Url { get; set; } + /// + /// The url of the link to create. + /// + public string Url { get; set; } - /// - /// The target of the link to create. - /// - public string Target { get; set; } + /// + /// The target of the link to create. + /// + public string Target { get; set; } - /// - /// The raw html to display for this link. - /// - public string Html { get; set; } - } + /// + /// The raw html to display for this link. + /// + public string Html { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs index ad255bc864b..f310047cfcb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs @@ -1,17 +1,16 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.Models +namespace OrchardCore.Menu.Models; + +public class LinkMenuItemPart : ContentPart { - public class LinkMenuItemPart : ContentPart - { - /// - /// The url of the link to create. - /// - public string Url { get; set; } + /// + /// The url of the link to create. + /// + public string Url { get; set; } - /// - /// The target of the link to create. - /// - public string Target { get; set; } - } + /// + /// The target of the link to create. + /// + public string Target { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuItemsListPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuItemsListPart.cs index ee60a48576d..6cf2d170c59 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuItemsListPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuItemsListPart.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.Models +namespace OrchardCore.Menu.Models; + +// A content item with this part can have menu items. +// This part is automatically added to all menus. +public class MenuItemsListPart : ContentPart { - // A content item with this part can have menu items. - // This part is automatically added to all menus. - public class MenuItemsListPart : ContentPart - { - public List MenuItems { get; set; } = []; - } + public List MenuItems { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuPart.cs index e728ea35723..8f826287747 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/MenuPart.cs @@ -1,9 +1,8 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Menu.Models +namespace OrchardCore.Menu.Models; + +// This part is added automatically to all menus +public class MenuPart : ContentPart { - // This part is added automatically to all menus - public class MenuPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettings.cs index 2e853ec832a..1e5d2c2de95 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettings.cs @@ -1,13 +1,12 @@ using System.ComponentModel; -namespace OrchardCore.Menu.Settings +namespace OrchardCore.Menu.Settings; + +public class HtmlMenuItemPartSettings { - public class HtmlMenuItemPartSettings - { - /// - /// Whether to sanitize the html input. - /// - [DefaultValue(true)] - public bool SanitizeHtml { get; set; } = true; - } + /// + /// Whether to sanitize the html input. + /// + [DefaultValue(true)] + public bool SanitizeHtml { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettingsDisplayDriver.cs index 809ecc95b37..20c80a8e9ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Settings/HtmlMenuItemPartSettingsDisplayDriver.cs @@ -6,32 +6,31 @@ using OrchardCore.Menu.Models; using OrchardCore.Menu.ViewModels; -namespace OrchardCore.Menu.Settings +namespace OrchardCore.Menu.Settings; + +public sealed class HtmlMenuItemPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class HtmlMenuItemPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("HtmlMenuItemPartSettings_Edit", model => { - return Initialize("HtmlMenuItemPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.SanitizeHtml = settings.SanitizeHtml; - }).Location("Content:20"); - } + model.SanitizeHtml = settings.SanitizeHtml; + }).Location("Content:20"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new HtmlMenuItemPartSettingsViewModel(); - var settings = new HtmlMenuItemPartSettings(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new HtmlMenuItemPartSettingsViewModel(); + var settings = new HtmlMenuItemPartSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SanitizeHtml = model.SanitizeHtml; + settings.SanitizeHtml = model.SanitizeHtml; - context.Builder.WithSettings(settings); + context.Builder.WithSettings(settings); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Startup.cs index d283cebfa3a..f2aa2264d8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Startup.cs @@ -15,40 +15,39 @@ using OrchardCore.Navigation; using OrchardCore.Security.Permissions; -namespace OrchardCore.Menu +namespace OrchardCore.Menu; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddScoped(); + services.AddScoped(); - // MenuPart - services.AddScoped(); - services.AddContentPart() - .UseDisplayDriver(); + // MenuPart + services.AddScoped(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddContentPart(); + services.AddContentPart(); - // LinkMenuItemPart - services.AddContentPart() - .UseDisplayDriver(); + // LinkMenuItemPart + services.AddContentPart() + .UseDisplayDriver(); - // ContentMenuItemPart - services.AddContentPart() - .UseDisplayDriver(); + // ContentMenuItemPart + services.AddContentPart() + .UseDisplayDriver(); - // HtmlMenuItemPart - services.AddContentPart() - .UseDisplayDriver(); - services.AddScoped(); + // HtmlMenuItemPart + services.AddContentPart() + .UseDisplayDriver(); + services.AddScoped(); - services.AddTagHelpers(); - } + services.AddTagHelpers(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/TagHelper/MenuTagHelper.cs b/src/OrchardCore.Modules/OrchardCore.Menu/TagHelper/MenuTagHelper.cs index 2fead4f6d67..76a8378e342 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/TagHelper/MenuTagHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/TagHelper/MenuTagHelper.cs @@ -2,15 +2,14 @@ using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.TagHelpers; -namespace OrchardCore.Menu.TagHelpers +namespace OrchardCore.Menu.TagHelpers; + +[HtmlTargetElement("menu")] +public class MenuTagHelper : BaseShapeTagHelper { - [HtmlTargetElement("menu")] - public class MenuTagHelper : BaseShapeTagHelper + public MenuTagHelper(IShapeFactory shapeFactory, IDisplayHelper displayHelper) + : base(shapeFactory, displayHelper) { - public MenuTagHelper(IShapeFactory shapeFactory, IDisplayHelper displayHelper) - : base(shapeFactory, displayHelper) - { - Type = "Menu"; - } + Type = "Menu"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/ContentMenuItemPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/ContentMenuItemPartEditViewModel.cs index a0a57d5dde1..890f98941db 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/ContentMenuItemPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/ContentMenuItemPartEditViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.ViewModels +namespace OrchardCore.Menu.ViewModels; + +public class ContentMenuItemPartEditViewModel { - public class ContentMenuItemPartEditViewModel - { - public string Name { get; set; } + public string Name { get; set; } - [BindNever] - public ContentMenuItemPart MenuItemPart { get; set; } - } + [BindNever] + public ContentMenuItemPart MenuItemPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs index 3a24a2c42f3..8f5bef23d60 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.ViewModels +namespace OrchardCore.Menu.ViewModels; + +public class HtmlMenuItemPartEditViewModel { - public class HtmlMenuItemPartEditViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public string Url { get; set; } + public string Url { get; set; } - public string Target { get; set; } + public string Target { get; set; } - public string Html { get; set; } + public string Html { get; set; } - [BindNever] - public HtmlMenuItemPart MenuItemPart { get; set; } - } + [BindNever] + public HtmlMenuItemPart MenuItemPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartSettingsViewModel.cs index 9f3d697fd6a..efc7cc50f49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartSettingsViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Menu.ViewModels +namespace OrchardCore.Menu.ViewModels; + +public class HtmlMenuItemPartSettingsViewModel { - public class HtmlMenuItemPartSettingsViewModel - { - public bool SanitizeHtml { get; set; } - } + public bool SanitizeHtml { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs index 301701c5b93..22f72fc8e3a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.ViewModels +namespace OrchardCore.Menu.ViewModels; + +public class LinkMenuItemPartEditViewModel { - public class LinkMenuItemPartEditViewModel - { - public string Name { get; set; } + public string Name { get; set; } - public string Url { get; set; } + public string Url { get; set; } - public string Target { get; set; } + public string Target { get; set; } - [BindNever] - public LinkMenuItemPart MenuItemPart { get; set; } - } + [BindNever] + public LinkMenuItemPart MenuItemPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/MenuPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/MenuPartEditViewModel.cs index 6e4bbd89d4e..5aa855b66e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/MenuPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/MenuPartEditViewModel.cs @@ -3,16 +3,15 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Menu.Models; -namespace OrchardCore.Menu.ViewModels +namespace OrchardCore.Menu.ViewModels; + +public class MenuPartEditViewModel { - public class MenuPartEditViewModel - { - public string Hierarchy { get; set; } + public string Hierarchy { get; set; } - [BindNever] - public MenuPart MenuPart { get; set; } + [BindNever] + public MenuPart MenuPart { get; set; } - [BindNever] - public IEnumerable MenuItemContentTypes { get; set; } - } + [BindNever] + public IEnumerable MenuItemContentTypes { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/AdminMenuMicrosoftAccount.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/AdminMenuMicrosoftAccount.cs index 8a7a7657419..33b8b8444e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/AdminMenuMicrosoftAccount.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/AdminMenuMicrosoftAccount.cs @@ -3,77 +3,76 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Microsoft.Authentication +namespace OrchardCore.Microsoft.Authentication; + +public sealed class AdminMenuMicrosoftAccount : INavigationProvider { - public sealed class AdminMenuMicrosoftAccount : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", MicrosoftAuthenticationConstants.Features.MicrosoftAccount }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", MicrosoftAuthenticationConstants.Features.MicrosoftAccount }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenuMicrosoftAccount(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenuMicrosoftAccount(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Security"], security => security - .Add(S["Authentication"], authentication => authentication - .Add(S["Microsoft"], S["Microsoft"].PrefixPosition(), microsoft => microsoft - .AddClass("microsoft") - .Id("microsoft") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageMicrosoftAuthentication) - .LocalNav() - ) + builder + .Add(S["Security"], security => security + .Add(S["Authentication"], authentication => authentication + .Add(S["Microsoft"], S["Microsoft"].PrefixPosition(), microsoft => microsoft + .AddClass("microsoft") + .Id("microsoft") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageMicrosoftAuthentication) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } +} - public sealed class AdminMenuAAD : INavigationProvider +public sealed class AdminMenuAAD : INavigationProvider +{ + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", MicrosoftAuthenticationConstants.Features.AAD }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", MicrosoftAuthenticationConstants.Features.AAD }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenuAAD(IStringLocalizer localizer) => S = localizer; + public AdminMenuAAD(IStringLocalizer localizer) => S = localizer; - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Security"], security => security - .Add(S["Authentication"], authentication => authentication - .Add(S["Microsoft Entra ID"], S["Microsoft Entra ID"].PrefixPosition(), client => client - .AddClass("microsoft-entra-id").Id("microsoft-entra-id") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageMicrosoftAuthentication) - .LocalNav()) - )); - return Task.CompletedTask; } + + builder + .Add(S["Security"], security => security + .Add(S["Authentication"], authentication => authentication + .Add(S["Microsoft Entra ID"], S["Microsoft Entra ID"].PrefixPosition(), client => client + .AddClass("microsoft-entra-id").Id("microsoft-entra-id") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageMicrosoftAuthentication) + .LocalNav()) + )); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/AzureADOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/AzureADOptionsConfiguration.cs index 186228b421d..33d58ac4afb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/AzureADOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/AzureADOptionsConfiguration.cs @@ -9,87 +9,86 @@ using MicrosoftIdentityDefaults = Microsoft.Identity.Web.Constants; -namespace OrchardCore.Microsoft.Authentication.Configuration +namespace OrchardCore.Microsoft.Authentication.Configuration; + +public class AzureADOptionsConfiguration : + IConfigureOptions, + IConfigureNamedOptions, + IConfigureNamedOptions { - public class AzureADOptionsConfiguration : - IConfigureOptions, - IConfigureNamedOptions, - IConfigureNamedOptions + public const string AzureAdOpenIdConnectScheme = MicrosoftIdentityDefaults.AzureAd + OpenIdConnectDefaults.AuthenticationScheme; + + private readonly AzureADSettings _azureADSettings; + private readonly ILogger _logger; + + public AzureADOptionsConfiguration(IOptions azureADSettings, ILogger logger) { - public const string AzureAdOpenIdConnectScheme = MicrosoftIdentityDefaults.AzureAd + OpenIdConnectDefaults.AuthenticationScheme; + _azureADSettings = azureADSettings.Value; + _logger = logger; + } - private readonly AzureADSettings _azureADSettings; - private readonly ILogger _logger; + public void Configure(AuthenticationOptions options) + { + var settings = _azureADSettings; + if (settings == null) + { + return; + } - public AzureADOptionsConfiguration(IOptions azureADSettings, ILogger logger) + if (string.IsNullOrWhiteSpace(settings.AppId) || string.IsNullOrWhiteSpace(settings.TenantId)) { - _azureADSettings = azureADSettings.Value; - _logger = logger; + _logger.LogWarning("The AzureAD login provider is enabled but not configured."); + + return; } - public void Configure(AuthenticationOptions options) + // Register the OpenID Connect client handler in the authentication handlers collection. + options.AddScheme(Constants.AzureAd, builder => + { + builder.DisplayName = settings.DisplayName; + builder.HandlerType = typeof(PolicySchemeHandler); + }); + + options.AddScheme(AzureAdOpenIdConnectScheme, builder => { - var settings = _azureADSettings; - if (settings == null) - { - return; - } - - if (string.IsNullOrWhiteSpace(settings.AppId) || string.IsNullOrWhiteSpace(settings.TenantId)) - { - _logger.LogWarning("The AzureAD login provider is enabled but not configured."); - - return; - } - - // Register the OpenID Connect client handler in the authentication handlers collection. - options.AddScheme(Constants.AzureAd, builder => - { - builder.DisplayName = settings.DisplayName; - builder.HandlerType = typeof(PolicySchemeHandler); - }); - - options.AddScheme(AzureAdOpenIdConnectScheme, builder => - { - builder.HandlerType = typeof(OpenIdConnectHandler); - }); + builder.HandlerType = typeof(OpenIdConnectHandler); + }); + } + + public void Configure(string name, MicrosoftIdentityOptions options) + { + if (!string.Equals(name, MicrosoftIdentityDefaults.AzureAd, StringComparison.Ordinal)) + { + return; } - public void Configure(string name, MicrosoftIdentityOptions options) + var loginSettings = _azureADSettings; + if (loginSettings == null) { - if (!string.Equals(name, MicrosoftIdentityDefaults.AzureAd, StringComparison.Ordinal)) - { - return; - } - - var loginSettings = _azureADSettings; - if (loginSettings == null) - { - return; - } - - options.ClientId = loginSettings.AppId; - options.TenantId = loginSettings.TenantId; - options.Instance = "https://login.microsoftonline.com/"; - - if (loginSettings.CallbackPath.HasValue) - { - options.CallbackPath = loginSettings.CallbackPath; - } + return; } - public void Configure(MicrosoftIdentityOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.ClientId = loginSettings.AppId; + options.TenantId = loginSettings.TenantId; + options.Instance = "https://login.microsoftonline.com/"; - public void Configure(string name, PolicySchemeOptions options) + if (loginSettings.CallbackPath.HasValue) { - if (!string.Equals(name, MicrosoftIdentityDefaults.AzureAd, StringComparison.Ordinal)) - { - return; - } + options.CallbackPath = loginSettings.CallbackPath; + } + } + + public void Configure(MicrosoftIdentityOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); - options.ForwardDefault = "Identity.External"; - options.ForwardChallenge = AzureAdOpenIdConnectScheme; + public void Configure(string name, PolicySchemeOptions options) + { + if (!string.Equals(name, MicrosoftIdentityDefaults.AzureAd, StringComparison.Ordinal)) + { + return; } - public void Configure(PolicySchemeOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + + options.ForwardDefault = "Identity.External"; + options.ForwardChallenge = AzureAdOpenIdConnectScheme; } + public void Configure(PolicySchemeOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/CookieOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/CookieOptionsConfiguration.cs index c525a189e97..5d2d6b3544f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/CookieOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/CookieOptionsConfiguration.cs @@ -4,36 +4,35 @@ using Microsoft.Extensions.Options; using MicrosoftIdentityDefaults = Microsoft.Identity.Web.Constants; -namespace OrchardCore.Microsoft.Authentication.Configuration +namespace OrchardCore.Microsoft.Authentication.Configuration; + +internal sealed class CookieOptionsConfiguration : IConfigureNamedOptions { - internal sealed class CookieOptionsConfiguration : IConfigureNamedOptions - { - private readonly string _tenantPrefix; + private readonly string _tenantPrefix; - public CookieOptionsConfiguration(IHttpContextAccessor httpContextAccessor) + public CookieOptionsConfiguration(IHttpContextAccessor httpContextAccessor) + { + var pathBase = httpContextAccessor.HttpContext?.Request.PathBase ?? PathString.Empty; + if (!pathBase.HasValue) { - var pathBase = httpContextAccessor.HttpContext?.Request.PathBase ?? PathString.Empty; - if (!pathBase.HasValue) - { - pathBase = "/"; - } - - _tenantPrefix = pathBase; + pathBase = "/"; } - public void Configure(string name, CookieAuthenticationOptions options) - { - if (name != "Identity.External") - { - return; - } + _tenantPrefix = pathBase; + } - options.Cookie.Path = _tenantPrefix; - options.LoginPath = $"~/AzureAD/Account/SignIn/{MicrosoftIdentityDefaults.AzureAd}"; - options.LogoutPath = $"~/AzureAD/Account/SignOut/{MicrosoftIdentityDefaults.AzureAd}"; - options.AccessDeniedPath = "~/AzureAD/Account/AccessDenied"; + public void Configure(string name, CookieAuthenticationOptions options) + { + if (name != "Identity.External") + { + return; } - public void Configure(CookieAuthenticationOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.Cookie.Path = _tenantPrefix; + options.LoginPath = $"~/AzureAD/Account/SignIn/{MicrosoftIdentityDefaults.AzureAd}"; + options.LogoutPath = $"~/AzureAD/Account/SignOut/{MicrosoftIdentityDefaults.AzureAd}"; + options.AccessDeniedPath = "~/AzureAD/Account/AccessDenied"; } + + public void Configure(CookieAuthenticationOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/MicrosoftAccountOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/MicrosoftAccountOptionsConfiguration.cs index afa7b295b10..9bbbe072e03 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/MicrosoftAccountOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/MicrosoftAccountOptionsConfiguration.cs @@ -7,81 +7,80 @@ using Microsoft.Extensions.Options; using OrchardCore.Microsoft.Authentication.Settings; -namespace OrchardCore.Microsoft.Authentication.Configuration +namespace OrchardCore.Microsoft.Authentication.Configuration; + +public class MicrosoftAccountOptionsConfiguration : + IConfigureOptions, + IConfigureNamedOptions { - public class MicrosoftAccountOptionsConfiguration : - IConfigureOptions, - IConfigureNamedOptions + private readonly MicrosoftAccountSettings _microsoftAccountSettings; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ILogger _logger; + + public MicrosoftAccountOptionsConfiguration( + IOptions microsoftAccountSettings, + IDataProtectionProvider dataProtectionProvider, + ILogger logger) { - private readonly MicrosoftAccountSettings _microsoftAccountSettings; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly ILogger _logger; + _microsoftAccountSettings = microsoftAccountSettings.Value; + _dataProtectionProvider = dataProtectionProvider; + _logger = logger; + } - public MicrosoftAccountOptionsConfiguration( - IOptions microsoftAccountSettings, - IDataProtectionProvider dataProtectionProvider, - ILogger logger) + public void Configure(AuthenticationOptions options) + { + if (_microsoftAccountSettings == null) { - _microsoftAccountSettings = microsoftAccountSettings.Value; - _dataProtectionProvider = dataProtectionProvider; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) + if (string.IsNullOrWhiteSpace(_microsoftAccountSettings.AppId) || + string.IsNullOrWhiteSpace(_microsoftAccountSettings.AppSecret)) { - if (_microsoftAccountSettings == null) - { - return; - } - - if (string.IsNullOrWhiteSpace(_microsoftAccountSettings.AppId) || - string.IsNullOrWhiteSpace(_microsoftAccountSettings.AppSecret)) - { - _logger.LogWarning("The Microsoft login provider is enabled but not configured."); + _logger.LogWarning("The Microsoft login provider is enabled but not configured."); - return; - } - - // Register the OpenID Connect client handler in the authentication handlers collection. - options.AddScheme(MicrosoftAccountDefaults.AuthenticationScheme, builder => - { - builder.DisplayName = "Microsoft Account"; - builder.HandlerType = typeof(MicrosoftAccountHandler); - }); + return; } - public void Configure(string name, MicrosoftAccountOptions options) + // Register the OpenID Connect client handler in the authentication handlers collection. + options.AddScheme(MicrosoftAccountDefaults.AuthenticationScheme, builder => { - // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. - if (!string.Equals(name, MicrosoftAccountDefaults.AuthenticationScheme, StringComparison.Ordinal)) - { - return; - } + builder.DisplayName = "Microsoft Account"; + builder.HandlerType = typeof(MicrosoftAccountHandler); + }); + } - if (_microsoftAccountSettings == null) - { - return; - } + public void Configure(string name, MicrosoftAccountOptions options) + { + // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. + if (!string.Equals(name, MicrosoftAccountDefaults.AuthenticationScheme, StringComparison.Ordinal)) + { + return; + } - options.ClientId = _microsoftAccountSettings.AppId; + if (_microsoftAccountSettings == null) + { + return; + } - try - { - options.ClientSecret = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount).Unprotect(_microsoftAccountSettings.AppSecret); - } - catch - { - _logger.LogError("The Microsoft Account secret key could not be decrypted. It may have been encrypted using a different key."); - } + options.ClientId = _microsoftAccountSettings.AppId; - if (_microsoftAccountSettings.CallbackPath.HasValue) - { - options.CallbackPath = _microsoftAccountSettings.CallbackPath; - } + try + { + options.ClientSecret = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount).Unprotect(_microsoftAccountSettings.AppSecret); + } + catch + { + _logger.LogError("The Microsoft Account secret key could not be decrypted. It may have been encrypted using a different key."); + } - options.SaveTokens = _microsoftAccountSettings.SaveTokens; + if (_microsoftAccountSettings.CallbackPath.HasValue) + { + options.CallbackPath = _microsoftAccountSettings.CallbackPath; } - public void Configure(MicrosoftAccountOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.SaveTokens = _microsoftAccountSettings.SaveTokens; } + + public void Configure(MicrosoftAccountOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/OpenIdConnectOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/OpenIdConnectOptionsConfiguration.cs index 7ba2df61f96..7c139480030 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/OpenIdConnectOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Configuration/OpenIdConnectOptionsConfiguration.cs @@ -7,41 +7,40 @@ using OrchardCore.Microsoft.Authentication.Settings; using MicrosoftIdentityDefaults = Microsoft.Identity.Web.Constants; -namespace OrchardCore.Microsoft.Authentication.Configuration +namespace OrchardCore.Microsoft.Authentication.Configuration; + +internal sealed class OpenIdConnectOptionsConfiguration : IConfigureNamedOptions { - internal sealed class OpenIdConnectOptionsConfiguration : IConfigureNamedOptions + private readonly IOptionsMonitor _azureADOptions; + private readonly AzureADSettings _azureADSettings; + + public OpenIdConnectOptionsConfiguration( + IOptionsMonitor azureADOptions, + IOptions azureADSettings) { - private readonly IOptionsMonitor _azureADOptions; - private readonly AzureADSettings _azureADSettings; + _azureADOptions = azureADOptions; + _azureADSettings = azureADSettings.Value; + } - public OpenIdConnectOptionsConfiguration( - IOptionsMonitor azureADOptions, - IOptions azureADSettings) + public void Configure(string name, OpenIdConnectOptions options) + { + if (name != AzureADOptionsConfiguration.AzureAdOpenIdConnectScheme) { - _azureADOptions = azureADOptions; - _azureADSettings = azureADSettings.Value; + return; } - public void Configure(string name, OpenIdConnectOptions options) - { - if (name != AzureADOptionsConfiguration.AzureAdOpenIdConnectScheme) - { - return; - } + var azureADOptions = _azureADOptions.Get(MicrosoftIdentityDefaults.AzureAd); - var azureADOptions = _azureADOptions.Get(MicrosoftIdentityDefaults.AzureAd); - - options.ClientId = azureADOptions.ClientId; - options.ClientSecret = azureADOptions.ClientSecret; - options.Authority = new Uri(new Uri(azureADOptions.Instance), azureADOptions.TenantId).ToString(); - options.CallbackPath = azureADOptions.CallbackPath.HasValue ? azureADOptions.CallbackPath : options.CallbackPath; - options.SignedOutCallbackPath = azureADOptions.SignedOutCallbackPath.HasValue ? azureADOptions.SignedOutCallbackPath : options.SignedOutCallbackPath; - options.SignInScheme = "Identity.External"; - options.UseTokenLifetime = true; - options.SaveTokens = _azureADSettings.SaveTokens; - options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(options.Authority, options.Backchannel).Validate; - } - - public void Configure(OpenIdConnectOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + options.ClientId = azureADOptions.ClientId; + options.ClientSecret = azureADOptions.ClientSecret; + options.Authority = new Uri(new Uri(azureADOptions.Instance), azureADOptions.TenantId).ToString(); + options.CallbackPath = azureADOptions.CallbackPath.HasValue ? azureADOptions.CallbackPath : options.CallbackPath; + options.SignedOutCallbackPath = azureADOptions.SignedOutCallbackPath.HasValue ? azureADOptions.SignedOutCallbackPath : options.SignedOutCallbackPath; + options.SignInScheme = "Identity.External"; + options.UseTokenLifetime = true; + options.SaveTokens = _azureADSettings.SaveTokens; + options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetAadIssuerValidator(options.Authority, options.Backchannel).Validate; } + + public void Configure(OpenIdConnectOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentSource.cs index a252c36473d..2f9a42e2758 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentSource.cs @@ -5,31 +5,30 @@ using OrchardCore.Microsoft.Authentication.Services; using OrchardCore.Microsoft.Authentication.Settings; -namespace OrchardCore.Microsoft.Authentication.Deployment +namespace OrchardCore.Microsoft.Authentication.Deployment; + +public class AzureADDeploymentSource : IDeploymentSource { - public class AzureADDeploymentSource : IDeploymentSource + private readonly IAzureADService _azureADService; + + public AzureADDeploymentSource(IAzureADService azureADService) { - private readonly IAzureADService _azureADService; + _azureADService = azureADService; + } - public AzureADDeploymentSource(IAzureADService azureADService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AzureADDeploymentStep azureADStep) { - _azureADService = azureADService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not AzureADDeploymentStep azureADStep) - { - return; - } - - var settings = await _azureADService.GetSettingsAsync(); + var settings = await _azureADService.GetSettingsAsync(); - var obj = new JsonObject { ["name"] = nameof(AzureADSettings) }; + var obj = new JsonObject { ["name"] = nameof(AzureADSettings) }; - obj.Merge(JObject.FromObject(settings, JOptions.Default)); + obj.Merge(JObject.FromObject(settings, JOptions.Default)); - result.Steps.Add(obj); - } + result.Steps.Add(obj); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStep.cs index 2d01d1c6c10..7f62ee50bfd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Microsoft.Authentication.Deployment +namespace OrchardCore.Microsoft.Authentication.Deployment; + +/// +/// Adds Microsoft Entra ID settings to a . +/// +public class AzureADDeploymentStep : DeploymentStep { - /// - /// Adds Microsoft Entra ID settings to a . - /// - public class AzureADDeploymentStep : DeploymentStep + public AzureADDeploymentStep() { - public AzureADDeploymentStep() - { - Name = "Microsoft Entra ID"; - } + Name = "Microsoft Entra ID"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStepDriver.cs index d12c19921a0..1a377ccf48f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Deployment/AzureADDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Microsoft.Authentication.Deployment +namespace OrchardCore.Microsoft.Authentication.Deployment; + +public sealed class AzureADDeploymentStepDriver : DisplayDriver { - public sealed class AzureADDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AzureADDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AzureADDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AzureADDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AzureADDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AzureADDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AzureADDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AzureADDeploymentStep step, BuildEditorContext context) - { - return View("AzureADDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AzureADDeploymentStep step, BuildEditorContext context) + { + return View("AzureADDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/AzureADSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/AzureADSettingsDisplayDriver.cs index a5404b91a60..a52bf87c83d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/AzureADSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/AzureADSettingsDisplayDriver.cs @@ -9,70 +9,69 @@ using OrchardCore.Microsoft.Authentication.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Microsoft.Authentication.Drivers +namespace OrchardCore.Microsoft.Authentication.Drivers; + +public class AzureADSettingsDisplayDriver : SiteDisplayDriver { - public class AzureADSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AzureADSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IHttpContextAccessor _httpContextAccessor; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _httpContextAccessor = httpContextAccessor; + } + + protected override string SettingsGroupId + => MicrosoftAuthenticationConstants.Features.AAD; - public AzureADSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ISite site, AzureADSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _httpContextAccessor = httpContextAccessor; + return null; } - protected override string SettingsGroupId - => MicrosoftAuthenticationConstants.Features.AAD; - - public override async Task EditAsync(ISite site, AzureADSettings settings, BuildEditorContext context) + return Initialize("MicrosoftEntraIDSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) + model.DisplayName = settings.DisplayName; + model.AppId = settings.AppId; + model.TenantId = settings.TenantId; + model.SaveTokens = settings.SaveTokens; + if (settings.CallbackPath.HasValue) { - return null; + model.CallbackPath = settings.CallbackPath.Value; } + }).Location("Content:0") + .OnGroup(SettingsGroupId); + } - return Initialize("MicrosoftEntraIDSettings_Edit", model => - { - model.DisplayName = settings.DisplayName; - model.AppId = settings.AppId; - model.TenantId = settings.TenantId; - model.SaveTokens = settings.SaveTokens; - if (settings.CallbackPath.HasValue) - { - model.CallbackPath = settings.CallbackPath.Value; - } - }).Location("Content:0") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, AzureADSettings settings, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, AzureADSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) - { - return null; - } + return null; + } - var model = new AzureADSettingsViewModel(); + var model = new AzureADSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.DisplayName = model.DisplayName; - settings.AppId = model.AppId; - settings.TenantId = model.TenantId; - settings.CallbackPath = model.CallbackPath; - settings.SaveTokens = model.SaveTokens; + settings.DisplayName = model.DisplayName; + settings.AppId = model.AppId; + settings.TenantId = model.TenantId; + settings.CallbackPath = model.CallbackPath; + settings.SaveTokens = model.SaveTokens; - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/MicrosoftAccountSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/MicrosoftAccountSettingsDisplayDriver.cs index 477bcf932a8..e16d7a9ee5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/MicrosoftAccountSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Drivers/MicrosoftAccountSettingsDisplayDriver.cs @@ -12,96 +12,95 @@ using OrchardCore.Microsoft.Authentication.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Microsoft.Authentication.Drivers +namespace OrchardCore.Microsoft.Authentication.Drivers; + +public sealed class MicrosoftAccountSettingsDisplayDriver : SiteDisplayDriver { - public sealed class MicrosoftAccountSettingsDisplayDriver : SiteDisplayDriver + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public MicrosoftAccountSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IDataProtectionProvider dataProtectionProvider, + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _dataProtectionProvider = dataProtectionProvider; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } + + protected override string SettingsGroupId + => MicrosoftAuthenticationConstants.Features.MicrosoftAccount; - public MicrosoftAccountSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IDataProtectionProvider dataProtectionProvider, - IHttpContextAccessor httpContextAccessor, - ILogger logger) + public override async Task EditAsync(ISite site, MicrosoftAccountSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _dataProtectionProvider = dataProtectionProvider; - _httpContextAccessor = httpContextAccessor; - _logger = logger; + return null; } - protected override string SettingsGroupId - => MicrosoftAuthenticationConstants.Features.MicrosoftAccount; - - public override async Task EditAsync(ISite site, MicrosoftAccountSettings settings, BuildEditorContext context) + return Initialize("MicrosoftAccountSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) - { - return null; - } - - return Initialize("MicrosoftAccountSettings_Edit", model => + model.AppId = settings.AppId; + if (!string.IsNullOrWhiteSpace(settings.AppSecret)) { - model.AppId = settings.AppId; - if (!string.IsNullOrWhiteSpace(settings.AppSecret)) + try { - try - { - var protector = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount); - model.AppSecret = protector.Unprotect(settings.AppSecret); - } - catch (CryptographicException) - { - _logger.LogError("The app secret could not be decrypted. It may have been encrypted using a different key."); - model.AppSecret = string.Empty; - model.HasDecryptionError = true; - } + var protector = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount); + model.AppSecret = protector.Unprotect(settings.AppSecret); } - else + catch (CryptographicException) { + _logger.LogError("The app secret could not be decrypted. It may have been encrypted using a different key."); model.AppSecret = string.Empty; + model.HasDecryptionError = true; } - if (settings.CallbackPath.HasValue) - { - model.CallbackPath = settings.CallbackPath.Value; - } - model.SaveTokens = settings.SaveTokens; - }).Location("Content:5") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, MicrosoftAccountSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) + } + else { - return null; + model.AppSecret = string.Empty; } + if (settings.CallbackPath.HasValue) + { + model.CallbackPath = settings.CallbackPath.Value; + } + model.SaveTokens = settings.SaveTokens; + }).Location("Content:5") + .OnGroup(SettingsGroupId); + } - var model = new MicrosoftAccountSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - settings.AppId = model.AppId; - settings.CallbackPath = model.CallbackPath; - settings.SaveTokens = model.SaveTokens; + public override async Task UpdateAsync(ISite site, MicrosoftAccountSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageMicrosoftAuthentication)) + { + return null; + } - if (context.Updater.ModelState.IsValid) - { - var protector = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount); + var model = new MicrosoftAccountSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.AppSecret = protector.Protect(model.AppSecret); - } + settings.AppId = model.AppId; + settings.CallbackPath = model.CallbackPath; + settings.SaveTokens = model.SaveTokens; - _shellReleaseManager.RequestRelease(); + if (context.Updater.ModelState.IsValid) + { + var protector = _dataProtectionProvider.CreateProtector(MicrosoftAuthenticationConstants.Features.MicrosoftAccount); - return await EditAsync(site, settings, context); + settings.AppSecret = protector.Protect(model.AppSecret); } + + _shellReleaseManager.RequestRelease(); + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Extensions/OrchardCoreBuilderExtensions.cs index 83494f8fb05..bc6e014a599 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,32 +2,31 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Microsoft.Authentication.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureMicrosoftAccountSettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureMicrosoftAccountSettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Microsoft_Authentication_MicrosoftAccount"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Microsoft_Authentication_MicrosoftAccount"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; + } - public static OrchardCoreBuilder ConfigureAzureADSettings(this OrchardCoreBuilder builder) + public static OrchardCoreBuilder ConfigureAzureADSettings(this OrchardCoreBuilder builder) + { + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Microsoft_Authentication_AzureAD"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Microsoft_Authentication_AzureAD"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MIcrosoftAuthenticationConstants.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MIcrosoftAuthenticationConstants.cs index 2d8ee60e6c7..44992dbed63 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MIcrosoftAuthenticationConstants.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MIcrosoftAuthenticationConstants.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Microsoft.Authentication +namespace OrchardCore.Microsoft.Authentication; + +public static class MicrosoftAuthenticationConstants { - public static class MicrosoftAuthenticationConstants + public static class Features { - public static class Features - { - public const string MicrosoftAccount = "OrchardCore.Microsoft.Authentication.MicrosoftAccount"; - public const string AAD = "OrchardCore.Microsoft.Authentication.AzureAD"; - } + public const string MicrosoftAccount = "OrchardCore.Microsoft.Authentication.MicrosoftAccount"; + public const string AAD = "OrchardCore.Microsoft.Authentication.AzureAD"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MicrosoftAccountStartup.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MicrosoftAccountStartup.cs index a68164ffc0b..826174b7dad 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MicrosoftAccountStartup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/MicrosoftAccountStartup.cs @@ -19,70 +19,69 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Microsoft.Authentication +namespace OrchardCore.Microsoft.Authentication; + +[Feature(MicrosoftAuthenticationConstants.Features.MicrosoftAccount)] +public sealed class MicrosoftAccountStartup : StartupBase { - [Feature(MicrosoftAuthenticationConstants.Features.MicrosoftAccount)] - public sealed class MicrosoftAccountStartup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.TryAddEnumerable(new ServiceDescriptor(typeof(IPermissionProvider), typeof(Permissions), ServiceLifetime.Scoped)); + services.TryAddEnumerable(new ServiceDescriptor(typeof(IPermissionProvider), typeof(Permissions), ServiceLifetime.Scoped)); - services.AddSingleton(); - services.AddScoped, MicrosoftAccountSettingsDisplayDriver>(); - services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddSingleton(); + services.AddScoped, MicrosoftAccountSettingsDisplayDriver>(); + services.AddScoped(); + services.AddRecipeExecutionStep(); - services.AddTransient, MicrosoftAccountSettingsConfiguration>(); + services.AddTransient, MicrosoftAccountSettingsConfiguration>(); - // Register the options initializers required by the Microsoft Account Handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers: - ServiceDescriptor.Transient, MicrosoftAccountOptionsConfiguration>(), - ServiceDescriptor.Transient, MicrosoftAccountOptionsConfiguration>(), - // Built-in initializers: - ServiceDescriptor.Transient, OAuthPostConfigureOptions>() - }); - } + // Register the options initializers required by the Microsoft Account Handler. + services.TryAddEnumerable(new[] + { + // Orchard-specific initializers: + ServiceDescriptor.Transient, MicrosoftAccountOptionsConfiguration>(), + ServiceDescriptor.Transient, MicrosoftAccountOptionsConfiguration>(), + // Built-in initializers: + ServiceDescriptor.Transient, OAuthPostConfigureOptions>() + }); } +} - [Feature(MicrosoftAuthenticationConstants.Features.AAD)] - public sealed class AzureADStartup : StartupBase +[Feature(MicrosoftAuthenticationConstants.Features.AAD)] +public sealed class AzureADStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.TryAddEnumerable(new ServiceDescriptor(typeof(IPermissionProvider), typeof(Permissions), ServiceLifetime.Scoped)); + services.TryAddEnumerable(new ServiceDescriptor(typeof(IPermissionProvider), typeof(Permissions), ServiceLifetime.Scoped)); - services.AddSingleton(); - services.AddRecipeExecutionStep(); + services.AddSingleton(); + services.AddRecipeExecutionStep(); - services.AddScoped, AzureADSettingsDisplayDriver>(); - services.AddScoped(); + services.AddScoped, AzureADSettingsDisplayDriver>(); + services.AddScoped(); - services.AddTransient, AzureADSettingsConfiguration>(); + services.AddTransient, AzureADSettingsConfiguration>(); - // Register the options initializers required by the Policy Scheme, Cookie and OpenId Connect Handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers. - ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), - ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), - ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), - ServiceDescriptor.Transient, OpenIdConnectOptionsConfiguration>(), + // Register the options initializers required by the Policy Scheme, Cookie and OpenId Connect Handler. + services.TryAddEnumerable(new[] + { + // Orchard-specific initializers. + ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), + ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), + ServiceDescriptor.Transient, AzureADOptionsConfiguration>(), + ServiceDescriptor.Transient, OpenIdConnectOptionsConfiguration>(), - // Built-in initializers: - ServiceDescriptor.Singleton, OpenIdConnectPostConfigureOptions>(), - }); - } + // Built-in initializers: + ServiceDescriptor.Singleton, OpenIdConnectPostConfigureOptions>(), + }); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/AzureADSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/AzureADSettingsStep.cs index 10b0e2956e9..a48271db7bc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/AzureADSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/AzureADSettingsStep.cs @@ -6,44 +6,43 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Microsoft.Authentication.Recipes +namespace OrchardCore.Microsoft.Authentication.Recipes; + +/// +/// This recipe step sets general Microsoft Entra ID settings. +/// +public sealed class AzureADSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets general Microsoft Entra ID settings. - /// - public sealed class AzureADSettingsStep : IRecipeStepHandler + private readonly IAzureADService _azureADService; + + public AzureADSettingsStep(IAzureADService azureADService) { - private readonly IAzureADService _azureADService; + _azureADService = azureADService; + } - public AzureADSettingsStep(IAzureADService azureADService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(AzureADSettings), StringComparison.OrdinalIgnoreCase)) { - _azureADService = azureADService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(AzureADSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _azureADService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _azureADService.LoadSettingsAsync(); - settings.AppId = model.AppId; - settings.TenantId = model.TenantId; - settings.DisplayName = model.DisplayName; - settings.CallbackPath = model.CallbackPath; + settings.AppId = model.AppId; + settings.TenantId = model.TenantId; + settings.DisplayName = model.DisplayName; + settings.CallbackPath = model.CallbackPath; - await _azureADService.UpdateSettingsAsync(settings); - } + await _azureADService.UpdateSettingsAsync(settings); } +} - public sealed class AzureADSettingsStepModel - { - public string DisplayName { get; set; } - public string AppId { get; set; } - public string TenantId { get; set; } - public string CallbackPath { get; set; } - } +public sealed class AzureADSettingsStepModel +{ + public string DisplayName { get; set; } + public string AppId { get; set; } + public string TenantId { get; set; } + public string CallbackPath { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/MicrosoftAccountSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/MicrosoftAccountSettingsStep.cs index e62b0cb0099..a10f9c68a40 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/MicrosoftAccountSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Recipes/MicrosoftAccountSettingsStep.cs @@ -6,42 +6,41 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Microsoft.Authentication.Recipes +namespace OrchardCore.Microsoft.Authentication.Recipes; + +/// +/// This recipe step sets Microsoft Account settings. +/// +public sealed class MicrosoftAccountSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets Microsoft Account settings. - /// - public sealed class MicrosoftAccountSettingsStep : IRecipeStepHandler + private readonly IMicrosoftAccountService _microsoftAccountService; + + public MicrosoftAccountSettingsStep(IMicrosoftAccountService microsoftAccountService) { - private readonly IMicrosoftAccountService _microsoftAccountService; + _microsoftAccountService = microsoftAccountService; + } - public MicrosoftAccountSettingsStep(IMicrosoftAccountService microsoftAccountService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(MicrosoftAccountSettings), StringComparison.OrdinalIgnoreCase)) { - _microsoftAccountService = microsoftAccountService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(MicrosoftAccountSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _microsoftAccountService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _microsoftAccountService.LoadSettingsAsync(); - settings.AppId = model.AppId; - settings.AppSecret = model.AppSecret; - settings.CallbackPath = model.CallbackPath; + settings.AppId = model.AppId; + settings.AppSecret = model.AppSecret; + settings.CallbackPath = model.CallbackPath; - await _microsoftAccountService.UpdateSettingsAsync(settings); - } + await _microsoftAccountService.UpdateSettingsAsync(settings); } +} - public sealed class MicrosoftAccountSettingsStepModel - { - public string AppId { get; set; } - public string AppSecret { get; set; } - public string CallbackPath { get; set; } - } +public sealed class MicrosoftAccountSettingsStepModel +{ + public string AppId { get; set; } + public string AppSecret { get; set; } + public string CallbackPath { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/AzureADService.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/AzureADService.cs index 62d91d3bc1e..b12f09ef8cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/AzureADService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/AzureADService.cs @@ -7,64 +7,63 @@ using OrchardCore.Microsoft.Authentication.Settings; using OrchardCore.Settings; -namespace OrchardCore.Microsoft.Authentication.Services +namespace OrchardCore.Microsoft.Authentication.Services; + +public class AzureADService : IAzureADService { - public class AzureADService : IAzureADService + private readonly ISiteService _siteService; + protected readonly IStringLocalizer S; + + public AzureADService( + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + _siteService = siteService; + S = stringLocalizer; + } - public AzureADService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } + public async Task UpdateSettingsAsync(AzureADSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(AzureADSettings settings) + var container = await _siteService.LoadSiteSettingsAsync(); + container.Alter(aspect => { - ArgumentNullException.ThrowIfNull(settings); + aspect.AppId = settings.AppId; + aspect.CallbackPath = settings.CallbackPath; + aspect.DisplayName = settings.DisplayName; + aspect.TenantId = settings.TenantId; + }); - var container = await _siteService.LoadSiteSettingsAsync(); - container.Alter(aspect => - { - aspect.AppId = settings.AppId; - aspect.CallbackPath = settings.CallbackPath; - aspect.DisplayName = settings.DisplayName; - aspect.TenantId = settings.TenantId; - }); + await _siteService.UpdateSiteSettingsAsync(container); + } - await _siteService.UpdateSiteSettingsAsync(container); - } + public IEnumerable ValidateSettings(AzureADSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public IEnumerable ValidateSettings(AzureADSettings settings) + if (string.IsNullOrWhiteSpace(settings.DisplayName)) { - ArgumentNullException.ThrowIfNull(settings); - - if (string.IsNullOrWhiteSpace(settings.DisplayName)) - { - yield return new ValidationResult(S["DisplayName is required"], new string[] { nameof(settings.DisplayName) }); - } + yield return new ValidationResult(S["DisplayName is required"], new string[] { nameof(settings.DisplayName) }); + } - if (string.IsNullOrWhiteSpace(settings.AppId)) - { - yield return new ValidationResult(S["AppId is required"], new string[] { nameof(settings.AppId) }); - } + if (string.IsNullOrWhiteSpace(settings.AppId)) + { + yield return new ValidationResult(S["AppId is required"], new string[] { nameof(settings.AppId) }); + } - if (string.IsNullOrWhiteSpace(settings.TenantId)) - { - yield return new ValidationResult(S["TenantId is required"], new string[] { nameof(settings.TenantId) }); - } + if (string.IsNullOrWhiteSpace(settings.TenantId)) + { + yield return new ValidationResult(S["TenantId is required"], new string[] { nameof(settings.TenantId) }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IAzureADService.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IAzureADService.cs index 822b30a282e..a66c834dac4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IAzureADService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IAzureADService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.Microsoft.Authentication.Settings; -namespace OrchardCore.Microsoft.Authentication.Services +namespace OrchardCore.Microsoft.Authentication.Services; + +public interface IAzureADService { - public interface IAzureADService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(AzureADSettings settings); - IEnumerable ValidateSettings(AzureADSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(AzureADSettings settings); + IEnumerable ValidateSettings(AzureADSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IMicrosoftAccountService.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IMicrosoftAccountService.cs index 5e5c1a07efc..83f1ab455fc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IMicrosoftAccountService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/IMicrosoftAccountService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.Microsoft.Authentication.Settings; -namespace OrchardCore.Microsoft.Authentication.Services +namespace OrchardCore.Microsoft.Authentication.Services; + +public interface IMicrosoftAccountService { - public interface IMicrosoftAccountService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(MicrosoftAccountSettings settings); - IEnumerable ValidateSettings(MicrosoftAccountSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(MicrosoftAccountSettings settings); + IEnumerable ValidateSettings(MicrosoftAccountSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/MicrosoftAccountService.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/MicrosoftAccountService.cs index 4828c1897d0..369fec3caf8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/MicrosoftAccountService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Services/MicrosoftAccountService.cs @@ -7,58 +7,57 @@ using OrchardCore.Microsoft.Authentication.Settings; using OrchardCore.Settings; -namespace OrchardCore.Microsoft.Authentication.Services +namespace OrchardCore.Microsoft.Authentication.Services; + +public class MicrosoftAccountService : IMicrosoftAccountService { - public class MicrosoftAccountService : IMicrosoftAccountService + private readonly ISiteService _siteService; + protected readonly IStringLocalizer S; + + public MicrosoftAccountService( + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + _siteService = siteService; + S = stringLocalizer; + } - public MicrosoftAccountService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } + public async Task UpdateSettingsAsync(MicrosoftAccountSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(MicrosoftAccountSettings settings) + var container = await _siteService.LoadSiteSettingsAsync(); + container.Alter(aspect => { - ArgumentNullException.ThrowIfNull(settings); + aspect.AppId = settings.AppId; + aspect.AppSecret = settings.AppSecret; + aspect.CallbackPath = settings.CallbackPath; + }); - var container = await _siteService.LoadSiteSettingsAsync(); - container.Alter(aspect => - { - aspect.AppId = settings.AppId; - aspect.AppSecret = settings.AppSecret; - aspect.CallbackPath = settings.CallbackPath; - }); + await _siteService.UpdateSiteSettingsAsync(container); + } - await _siteService.UpdateSiteSettingsAsync(container); - } + public IEnumerable ValidateSettings(MicrosoftAccountSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public IEnumerable ValidateSettings(MicrosoftAccountSettings settings) + if (string.IsNullOrWhiteSpace(settings.AppId)) { - ArgumentNullException.ThrowIfNull(settings); - - if (string.IsNullOrWhiteSpace(settings.AppId)) - { - yield return new ValidationResult(S["AppId is required"], new string[] { nameof(settings.AppId) }); - } + yield return new ValidationResult(S["AppId is required"], new string[] { nameof(settings.AppId) }); + } - if (string.IsNullOrWhiteSpace(settings.AppSecret)) - { - yield return new ValidationResult(S["AppSecret is required"], new string[] { nameof(settings.AppSecret) }); - } + if (string.IsNullOrWhiteSpace(settings.AppSecret)) + { + yield return new ValidationResult(S["AppSecret is required"], new string[] { nameof(settings.AppSecret) }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/AzureADSettings.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/AzureADSettings.cs index cf87240f01b..c9bcf9ff6e7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/AzureADSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/AzureADSettings.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Microsoft.Authentication.Settings +namespace OrchardCore.Microsoft.Authentication.Settings; + +public class AzureADSettings { - public class AzureADSettings - { - public string DisplayName { get; set; } - public string AppId { get; set; } - public string TenantId { get; set; } - public PathString CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public string DisplayName { get; set; } + public string AppId { get; set; } + public string TenantId { get; set; } + public PathString CallbackPath { get; set; } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/MicrosoftAccountSettings.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/MicrosoftAccountSettings.cs index 3655120635b..4a02e69e337 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/MicrosoftAccountSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/Settings/MicrosoftAccountSettings.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Microsoft.Authentication.Settings +namespace OrchardCore.Microsoft.Authentication.Settings; + +public class MicrosoftAccountSettings { - public class MicrosoftAccountSettings - { - public string AppId { get; set; } - public string AppSecret { get; set; } - public PathString CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public string AppId { get; set; } + public string AppSecret { get; set; } + public PathString CallbackPath { get; set; } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/AzureADSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/AzureADSettingsViewModel.cs index 49cf00cfa0e..2e3e1608e4a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/AzureADSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/AzureADSettingsViewModel.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Microsoft.Authentication.ViewModels +namespace OrchardCore.Microsoft.Authentication.ViewModels; + +public class AzureADSettingsViewModel { - public class AzureADSettingsViewModel - { - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - [Required(AllowEmptyStrings = false, ErrorMessage = "Application Id is required")] - public string AppId { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "Application Id is required")] + public string AppId { get; set; } - [Required(AllowEmptyStrings = false, ErrorMessage = "Tenant Id is required")] - public string TenantId { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "Tenant Id is required")] + public string TenantId { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackPath { get; set; } - public bool SaveTokens { get; set; } - } + public bool SaveTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/ErrorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/ErrorViewModel.cs index c111f9a74f7..98bcd7c33eb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/ErrorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/ErrorViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Facebook.ViewModels +namespace OrchardCore.Facebook.ViewModels; + +public class ErrorViewModel { - public class ErrorViewModel - { - public string Error { get; set; } + public string Error { get; set; } - public string ErrorDescription { get; set; } - } + public string ErrorDescription { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/MicrosoftAccountSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/MicrosoftAccountSettingsViewModel.cs index ac5e7595c40..cbac43c609a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/MicrosoftAccountSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Microsoft.Authentication/ViewModels/MicrosoftAccountSettingsViewModel.cs @@ -1,20 +1,19 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Microsoft.Authentication.ViewModels +namespace OrchardCore.Microsoft.Authentication.ViewModels; + +public class MicrosoftAccountSettingsViewModel { - public class MicrosoftAccountSettingsViewModel - { - [Required(AllowEmptyStrings = false, ErrorMessage = "Application Id is required")] - public string AppId { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "Application Id is required")] + public string AppId { get; set; } - [Required(AllowEmptyStrings = false, ErrorMessage = "Application Secret is required")] - public string AppSecret { get; set; } + [Required(AllowEmptyStrings = false, ErrorMessage = "Application Secret is required")] + public string AppSecret { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackPath { get; set; } - public bool SaveTokens { get; set; } + public bool SaveTokens { get; set; } - public bool HasDecryptionError { get; set; } - } + public bool HasDecryptionError { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/CurrentDbProfiler.cs b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/CurrentDbProfiler.cs index 82b13059870..346dea04f25 100644 --- a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/CurrentDbProfiler.cs +++ b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/CurrentDbProfiler.cs @@ -3,25 +3,24 @@ using System.Data.Common; using StackExchange.Profiling.Data; -namespace OrchardCore.MiniProfiler +namespace OrchardCore.MiniProfiler; + +internal sealed class CurrentDbProfiler : IDbProfiler { - internal sealed class CurrentDbProfiler : IDbProfiler - { - private Func GetProfiler { get; } - public CurrentDbProfiler(Func getProfiler) => GetProfiler = getProfiler; + private Func GetProfiler { get; } + public CurrentDbProfiler(Func getProfiler) => GetProfiler = getProfiler; - public bool IsActive => ((IDbProfiler)StackExchange.Profiling.MiniProfiler.Current)?.IsActive ?? false; + public bool IsActive => ((IDbProfiler)StackExchange.Profiling.MiniProfiler.Current)?.IsActive ?? false; - public void ExecuteFinish(IDbCommand profiledDbCommand, SqlExecuteType executeType, DbDataReader reader) => - GetProfiler()?.ExecuteFinish(profiledDbCommand, executeType, reader); + public void ExecuteFinish(IDbCommand profiledDbCommand, SqlExecuteType executeType, DbDataReader reader) => + GetProfiler()?.ExecuteFinish(profiledDbCommand, executeType, reader); - public void ExecuteStart(IDbCommand profiledDbCommand, SqlExecuteType executeType) => - GetProfiler()?.ExecuteStart(profiledDbCommand, executeType); + public void ExecuteStart(IDbCommand profiledDbCommand, SqlExecuteType executeType) => + GetProfiler()?.ExecuteStart(profiledDbCommand, executeType); - public void OnError(IDbCommand profiledDbCommand, SqlExecuteType executeType, Exception exception) => - GetProfiler()?.OnError(profiledDbCommand, executeType, exception); + public void OnError(IDbCommand profiledDbCommand, SqlExecuteType executeType, Exception exception) => + GetProfiler()?.OnError(profiledDbCommand, executeType, exception); - public void ReaderFinish(IDataReader reader) => - GetProfiler()?.ReaderFinish(reader); - } + public void ReaderFinish(IDataReader reader) => + GetProfiler()?.ReaderFinish(reader); } diff --git a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerConnectionFactory.cs b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerConnectionFactory.cs index 555af9f3146..4d3ca7436a0 100644 --- a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerConnectionFactory.cs +++ b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerConnectionFactory.cs @@ -3,27 +3,26 @@ using StackExchange.Profiling.Data; using YesSql; -namespace OrchardCore.MiniProfiler +namespace OrchardCore.MiniProfiler; + +internal sealed class MiniProfilerConnectionFactory : IConnectionFactory { - internal sealed class MiniProfilerConnectionFactory : IConnectionFactory - { - private static readonly string ConnectionName = nameof(ProfiledDbConnection).ToLower(); + private static readonly string ConnectionName = nameof(ProfiledDbConnection).ToLower(); - private readonly IConnectionFactory _factory; + private readonly IConnectionFactory _factory; - public Type DbConnectionType => typeof(ProfiledDbConnection); + public Type DbConnectionType => typeof(ProfiledDbConnection); - public MiniProfilerConnectionFactory(IConnectionFactory factory) - { - _factory = factory; - } + public MiniProfilerConnectionFactory(IConnectionFactory factory) + { + _factory = factory; + } - public DbConnection CreateConnection() - { - // Forward the call to the actual factory. - var connection = _factory.CreateConnection(); + public DbConnection CreateConnection() + { + // Forward the call to the actual factory. + var connection = _factory.CreateConnection(); - return new ProfiledDbConnection(connection, new CurrentDbProfiler(() => StackExchange.Profiling.MiniProfiler.Current)); - } + return new ProfiledDbConnection(connection, new CurrentDbProfiler(() => StackExchange.Profiling.MiniProfiler.Current)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerFilter.cs b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerFilter.cs index fc4a23394cb..329e8fbd048 100644 --- a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/MiniProfilerFilter.cs @@ -6,46 +6,45 @@ using OrchardCore.DisplayManagement.Layout; using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.MiniProfiler +namespace OrchardCore.MiniProfiler; + +public sealed class MiniProfilerFilter : IAsyncResultFilter { - public sealed class MiniProfilerFilter : IAsyncResultFilter + private readonly ILayoutAccessor _layoutAccessor; + private readonly IShapeFactory _shapeFactory; + private readonly IAuthorizationService _authorizationService; + + public MiniProfilerFilter( + ILayoutAccessor layoutAccessor, + IShapeFactory shapeFactory, + IAuthorizationService authorizationService) { - private readonly ILayoutAccessor _layoutAccessor; - private readonly IShapeFactory _shapeFactory; - private readonly IAuthorizationService _authorizationService; + _layoutAccessor = layoutAccessor; + _shapeFactory = shapeFactory; + _authorizationService = authorizationService; + } - public MiniProfilerFilter( - ILayoutAccessor layoutAccessor, - IShapeFactory shapeFactory, - IAuthorizationService authorizationService) + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + var viewMiniProfilerOnFrontEnd = await _authorizationService.AuthorizeAsync(context.HttpContext.User, Permissions.ViewMiniProfilerOnFrontEnd); + var viewMiniProfilerOnBackEnd = await _authorizationService.AuthorizeAsync(context.HttpContext.User, Permissions.ViewMiniProfilerOnBackEnd); + if ( + context.IsViewOrPageResult() && + ( + (viewMiniProfilerOnFrontEnd && !AdminAttribute.IsApplied(context.HttpContext)) || + (viewMiniProfilerOnBackEnd && AdminAttribute.IsApplied(context.HttpContext)) + ) + ) { - _layoutAccessor = layoutAccessor; - _shapeFactory = shapeFactory; - _authorizationService = authorizationService; - } + var layout = await _layoutAccessor.GetLayoutAsync(); + var footerZone = layout.Zones["Footer"]; - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) - { - var viewMiniProfilerOnFrontEnd = await _authorizationService.AuthorizeAsync(context.HttpContext.User, Permissions.ViewMiniProfilerOnFrontEnd); - var viewMiniProfilerOnBackEnd = await _authorizationService.AuthorizeAsync(context.HttpContext.User, Permissions.ViewMiniProfilerOnBackEnd); - if ( - context.IsViewOrPageResult() && - ( - (viewMiniProfilerOnFrontEnd && !AdminAttribute.IsApplied(context.HttpContext)) || - (viewMiniProfilerOnBackEnd && AdminAttribute.IsApplied(context.HttpContext)) - ) - ) + if (footerZone is Shape shape) { - var layout = await _layoutAccessor.GetLayoutAsync(); - var footerZone = layout.Zones["Footer"]; - - if (footerZone is Shape shape) - { - await shape.AddAsync(await _shapeFactory.CreateAsync("MiniProfiler")); - } + await shape.AddAsync(await _shapeFactory.CreateAsync("MiniProfiler")); } - - await next.Invoke(); } + + await next.Invoke(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/ShapeStep.cs b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/ShapeStep.cs index 2253d3834b1..e3d9f51f2ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/ShapeStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/ShapeStep.cs @@ -4,39 +4,38 @@ using OrchardCore.DisplayManagement.Implementation; using StackExchange.Profiling; -namespace OrchardCore.MiniProfiler +namespace OrchardCore.MiniProfiler; + +public class ShapeStep : IShapeDisplayEvents { - public class ShapeStep : IShapeDisplayEvents - { - private readonly Dictionary _timings = []; + private readonly Dictionary _timings = []; - public Task DisplayedAsync(ShapeDisplayContext context) + public Task DisplayedAsync(ShapeDisplayContext context) + { + if (_timings.TryGetValue(context, out var timing)) { - if (_timings.TryGetValue(context, out var timing)) - { - _timings.Remove(context); - timing.Dispose(); - } - - return Task.CompletedTask; + _timings.Remove(context); + timing.Dispose(); } - public Task DisplayingAsync(ShapeDisplayContext context) - { - var timing = StackExchange.Profiling.MiniProfiler.Current.Step($"Shape: {context.Shape.Metadata.Type}"); - _timings.Add(context, timing); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - public Task DisplayingFinalizedAsync(ShapeDisplayContext context) - { - if (_timings.TryGetValue(context, out var timing)) - { - _timings.Remove(context); - timing.Dispose(); - } + public Task DisplayingAsync(ShapeDisplayContext context) + { + var timing = StackExchange.Profiling.MiniProfiler.Current.Step($"Shape: {context.Shape.Metadata.Type}"); + _timings.Add(context, timing); + return Task.CompletedTask; + } - return Task.CompletedTask; + public Task DisplayingFinalizedAsync(ShapeDisplayContext context) + { + if (_timings.TryGetValue(context, out var timing)) + { + _timings.Remove(context); + timing.Dispose(); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/Startup.cs b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/Startup.cs index f8ab4ce7380..c0532b3a7b3 100644 --- a/src/OrchardCore.Modules/OrchardCore.MiniProfiler/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.MiniProfiler/Startup.cs @@ -8,35 +8,34 @@ using OrchardCore.Security.Permissions; using YesSql; -namespace OrchardCore.MiniProfiler +namespace OrchardCore.MiniProfiler; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - // Early in the pipeline to wrap all other middleware - public override int Order => -500; + // Early in the pipeline to wrap all other middleware + public override int Order => -500; - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + services.Configure((options) => { - services.Configure((options) => - { - options.Filters.Add(); - }); + options.Filters.Add(); + }); - services.AddScoped(); + services.AddScoped(); - services.AddMiniProfiler(); + services.AddMiniProfiler(); - services.AddScoped(); - } + services.AddScoped(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseMiniProfiler(); + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseMiniProfiler(); - var store = serviceProvider.GetService(); + var store = serviceProvider.GetService(); - // Wrap the current factory with MiniProfilerConnectionFactory. - store.Configuration.ConnectionFactory = new MiniProfilerConnectionFactory(store.Configuration.ConnectionFactory); - } + // Wrap the current factory with MiniProfilerConnectionFactory. + store.Configuration.ConnectionFactory = new MiniProfilerConnectionFactory(store.Configuration.ConnectionFactory); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Controllers/HomeController.cs b/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Controllers/HomeController.cs index dcd0e39e8e6..da8823cbb36 100644 --- a/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Controllers/HomeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Controllers/HomeController.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc; -namespace HelloWorld.Controllers +namespace HelloWorld.Controllers; + +public class HomeController : Controller { - public class HomeController : Controller + public IActionResult Index() { - public IActionResult Index() - { - return View(); - } + return View(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Startup.cs index 8f41ef0fc09..beab65f1f56 100644 --- a/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld/Startup.cs @@ -4,31 +4,30 @@ using Microsoft.Extensions.Configuration; using OrchardCore.Modules; -namespace OrchardCore.Mvc.HelloWorld +namespace OrchardCore.Mvc.HelloWorld; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly IConfiguration _configuration; + + public Startup(IConfiguration configuration) { - private readonly IConfiguration _configuration; + _configuration = configuration; + } - public Startup(IConfiguration configuration) + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + if (string.IsNullOrEmpty(_configuration["Sample"])) { - _configuration = configuration; + throw new Exception(":("); } - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - if (string.IsNullOrEmpty(_configuration["Sample"])) - { - throw new Exception(":("); - } - - routes.MapAreaControllerRoute - ( - name: "Home", - areaName: "OrchardCore.Mvc.HelloWorld", - pattern: "", - defaults: new { controller = "Home", action = "Index" } - ); - } + routes.MapAreaControllerRoute + ( + name: "Home", + areaName: "OrchardCore.Mvc.HelloWorld", + pattern: "", + defaults: new { controller = "Home", action = "Index" } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Navigation/NavigationShapes.cs b/src/OrchardCore.Modules/OrchardCore.Navigation/NavigationShapes.cs index d984364033c..27e7e864691 100644 --- a/src/OrchardCore.Modules/OrchardCore.Navigation/NavigationShapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.Navigation/NavigationShapes.cs @@ -8,104 +8,103 @@ using OrchardCore.DisplayManagement.Utilities; using OrchardCore.Mvc.Utilities; -namespace OrchardCore.Navigation +namespace OrchardCore.Navigation; + +public class NavigationShapes : ShapeTableProvider { - public class NavigationShapes : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("Navigation") - .OnDisplaying(displaying => - { - var menu = displaying.Shape; - var menuName = menu.GetProperty("MenuName"); - - menu.Classes.Add("menu-" + menuName.HtmlClassify()); - menu.Classes.Add("menu"); - menu.Metadata.Alternates.Add("Navigation__" + menuName.EncodeAlternateElement()); - }) - .OnProcessing(async context => + builder.Describe("Navigation") + .OnDisplaying(displaying => + { + var menu = displaying.Shape; + var menuName = menu.GetProperty("MenuName"); + + menu.Classes.Add("menu-" + menuName.HtmlClassify()); + menu.Classes.Add("menu"); + menu.Metadata.Alternates.Add("Navigation__" + menuName.EncodeAlternateElement()); + }) + .OnProcessing(async context => + { + var menu = context.Shape; + var menuName = menu.GetProperty("MenuName"); + + // Menu population is executed when processing the shape so that its value + // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor + // events and thus this code can be cached. + + if (menu is Shape shape && shape.HasItems) { - var menu = context.Shape; - var menuName = menu.GetProperty("MenuName"); - - // Menu population is executed when processing the shape so that its value - // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor - // events and thus this code can be cached. + return; + } - if (menu is Shape shape && shape.HasItems) - { - return; - } + var viewContextAccessor = context.ServiceProvider.GetRequiredService(); + var viewContext = viewContextAccessor.ViewContext; + var navigationManagers = context.ServiceProvider.GetServices(); + var shapeFactory = context.ServiceProvider.GetRequiredService(); + var httpContextAccessor = context.ServiceProvider.GetRequiredService(); - var viewContextAccessor = context.ServiceProvider.GetRequiredService(); - var viewContext = viewContextAccessor.ViewContext; - var navigationManagers = context.ServiceProvider.GetServices(); - var shapeFactory = context.ServiceProvider.GetRequiredService(); - var httpContextAccessor = context.ServiceProvider.GetRequiredService(); + foreach (var navigationManager in navigationManagers) + { + var menuItems = await navigationManager.BuildMenuAsync(menuName, viewContext); + var httpContext = httpContextAccessor.HttpContext; - foreach (var navigationManager in navigationManagers) + if (httpContext != null) { - var menuItems = await navigationManager.BuildMenuAsync(menuName, viewContext); - var httpContext = httpContextAccessor.HttpContext; + // adding query string parameters + var route = menu.GetProperty("RouteData"); + var routeData = new RouteValueDictionary(route.Values); + var query = httpContext.Request.Query; - if (httpContext != null) + if (query != null) { - // adding query string parameters - var route = menu.GetProperty("RouteData"); - var routeData = new RouteValueDictionary(route.Values); - var query = httpContext.Request.Query; - - if (query != null) + foreach (var pair in query) { - foreach (var pair in query) + if (pair.Key != null && !routeData.ContainsKey(pair.Key)) { - if (pair.Key != null && !routeData.ContainsKey(pair.Key)) - { - routeData[pair.Key] = pair.Value; - } + routeData[pair.Key] = pair.Value; } } } - - // TODO: Flag Selected menu item - await NavigationHelper.PopulateMenuAsync(shapeFactory, menu, menu, menuItems, viewContext); } - }); - builder.Describe("NavigationItem") - .OnDisplaying(displaying => - { - var menuItem = displaying.Shape; - var menu = menuItem.GetProperty("Menu"); - var menuName = menu.GetProperty("MenuName"); - var level = menuItem.GetProperty("Level"); + // TODO: Flag Selected menu item + await NavigationHelper.PopulateMenuAsync(shapeFactory, menu, menu, menuItems, viewContext); + } + }); - var encodedMenuName = menuName.EncodeAlternateElement(); + builder.Describe("NavigationItem") + .OnDisplaying(displaying => + { + var menuItem = displaying.Shape; + var menu = menuItem.GetProperty("Menu"); + var menuName = menu.GetProperty("MenuName"); + var level = menuItem.GetProperty("Level"); - menuItem.Metadata.Alternates.Add("NavigationItem__level__" + level); - menuItem.Metadata.Alternates.Add("NavigationItem__" + encodedMenuName); - menuItem.Metadata.Alternates.Add("NavigationItem__" + encodedMenuName + "__level__" + level); - }); + var encodedMenuName = menuName.EncodeAlternateElement(); - builder.Describe("NavigationItemLink") - .OnDisplaying(displaying => - { - var menuItem = displaying.Shape; - var menuName = menuItem.GetProperty("Menu").GetProperty("MenuName"); - var level = menuItem.GetProperty("Level"); + menuItem.Metadata.Alternates.Add("NavigationItem__level__" + level); + menuItem.Metadata.Alternates.Add("NavigationItem__" + encodedMenuName); + menuItem.Metadata.Alternates.Add("NavigationItem__" + encodedMenuName + "__level__" + level); + }); + + builder.Describe("NavigationItemLink") + .OnDisplaying(displaying => + { + var menuItem = displaying.Shape; + var menuName = menuItem.GetProperty("Menu").GetProperty("MenuName"); + var level = menuItem.GetProperty("Level"); - menuItem.Metadata.Alternates.Add("NavigationItemLink__level__" + level); + menuItem.Metadata.Alternates.Add("NavigationItemLink__level__" + level); - var encodedMenuName = menuName.EncodeAlternateElement(); + var encodedMenuName = menuName.EncodeAlternateElement(); - // NavigationItemLink__[MenuName] e.g. NavigationItemLink-Main-Menu - // NavigationItemLink__[MenuName]__level__[level] e.g. NavigationItemLink-Main-Menu-level-2 - menuItem.Metadata.Alternates.Add("NavigationItemLink__" + encodedMenuName); - menuItem.Metadata.Alternates.Add("NavigationItemLink__" + encodedMenuName + "__level__" + level); - }); + // NavigationItemLink__[MenuName] e.g. NavigationItemLink-Main-Menu + // NavigationItemLink__[MenuName]__level__[level] e.g. NavigationItemLink-Main-Menu-level-2 + menuItem.Metadata.Alternates.Add("NavigationItemLink__" + encodedMenuName); + menuItem.Metadata.Alternates.Add("NavigationItemLink__" + encodedMenuName + "__level__" + level); + }); - return ValueTask.CompletedTask; - } + return ValueTask.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Navigation/PagerShapesTableProvider.cs b/src/OrchardCore.Modules/OrchardCore.Navigation/PagerShapesTableProvider.cs index b31b4841a54..3d7235a2c4f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Navigation/PagerShapesTableProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Navigation/PagerShapesTableProvider.cs @@ -17,585 +17,584 @@ using OrchardCore.DisplayManagement.Shapes; using OrchardCore.DisplayManagement.Utilities; -namespace OrchardCore.Navigation +namespace OrchardCore.Navigation; + +public class PagerShapesTableProvider : ShapeTableProvider { - public class PagerShapesTableProvider : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - builder.Describe("Pager") - .OnCreated(created => - { - // Initializes the common properties of a Pager shape - // such that views can safely add values to them. - created.Shape.Properties["ItemClasses"] = new List(); - created.Shape.Properties["ItemAttributes"] = new Dictionary(); - }) - .OnDisplaying(displaying => + builder.Describe("Pager") + .OnCreated(created => + { + // Initializes the common properties of a Pager shape + // such that views can safely add values to them. + created.Shape.Properties["ItemClasses"] = new List(); + created.Shape.Properties["ItemAttributes"] = new Dictionary(); + }) + .OnDisplaying(displaying => + { + if (displaying.Shape.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - if (displaying.Shape.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager__" + pagerId.EncodeAlternateElement()); - }; - }); + displaying.Shape.Metadata.Alternates.Add("Pager__" + pagerId.EncodeAlternateElement()); + }; + }); - builder.Describe("PagerSlim") - .OnCreated(created => - { - // Initializes the common properties of a Pager shape - // such that views can safely add values to them. - created.Shape.Properties["ItemClasses"] = new List(); - created.Shape.Properties["ItemAttributes"] = new Dictionary(); - }) - .OnDisplaying(displaying => + builder.Describe("PagerSlim") + .OnCreated(created => + { + // Initializes the common properties of a Pager shape + // such that views can safely add values to them. + created.Shape.Properties["ItemClasses"] = new List(); + created.Shape.Properties["ItemAttributes"] = new Dictionary(); + }) + .OnDisplaying(displaying => + { + if (displaying.Shape.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - if (displaying.Shape.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager__" + pagerId.EncodeAlternateElement()); - }; - }); + displaying.Shape.Metadata.Alternates.Add("Pager__" + pagerId.EncodeAlternateElement()); + }; + }); - builder.Describe("Pager_Gap") - .OnDisplaying(displaying => + builder.Describe("Pager_Gap") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; + + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; + displaying.Shape.Metadata.Alternates.Add("Pager_Gap__" + pagerId.EncodeAlternateElement()); + } + }); - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_Gap__" + pagerId.EncodeAlternateElement()); - } - }); + builder.Describe("Pager_First") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; - builder.Describe("Pager_First") - .OnDisplaying(displaying => + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; + displaying.Shape.Metadata.Alternates.Add("Pager_First__" + pagerId.EncodeAlternateElement()); + } + }); - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_First__" + pagerId.EncodeAlternateElement()); - } - }); + builder.Describe("Pager_Previous") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; - builder.Describe("Pager_Previous") - .OnDisplaying(displaying => + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; + displaying.Shape.Metadata.Alternates.Add("Pager_Previous__" + pagerId.EncodeAlternateElement()); + } + }); - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_Previous__" + pagerId.EncodeAlternateElement()); - } - }); + builder.Describe("Pager_Next") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; - builder.Describe("Pager_Next") - .OnDisplaying(displaying => + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; + displaying.Shape.Metadata.Alternates.Add("Pager_Next__" + pagerId.EncodeAlternateElement()); + } + }); - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_Next__" + pagerId.EncodeAlternateElement()); - } - }); + builder.Describe("Pager_Last") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; - builder.Describe("Pager_Last") - .OnDisplaying(displaying => + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; + displaying.Shape.Metadata.Alternates.Add("Pager_Last__" + pagerId.EncodeAlternateElement()); + } + }); - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_Last__" + pagerId.EncodeAlternateElement()); - } - }); + builder.Describe("Pager_CurrentPage") + .OnDisplaying(displaying => + { + var shape = displaying.Shape; - builder.Describe("Pager_CurrentPage") - .OnDisplaying(displaying => + if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) { - var shape = displaying.Shape; - - if (shape.TryGetProperty("Pager", out IShape pager) && pager.TryGetProperty("PagerId", out string pagerId) && !string.IsNullOrEmpty(pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_CurrentPage__" + pagerId.EncodeAlternateElement()); - } - }); + displaying.Shape.Metadata.Alternates.Add("Pager_CurrentPage__" + pagerId.EncodeAlternateElement()); + } + }); - builder.Describe("Pager_Links") - .OnDisplaying(displaying => + builder.Describe("Pager_Links") + .OnDisplaying(displaying => + { + if (displaying.Shape.TryGetProperty("PagerId", out string pagerId)) { - if (displaying.Shape.TryGetProperty("PagerId", out string pagerId)) - { - displaying.Shape.Metadata.Alternates.Add("Pager_Links__" + pagerId.EncodeAlternateElement()); - } - }); + displaying.Shape.Metadata.Alternates.Add("Pager_Links__" + pagerId.EncodeAlternateElement()); + } + }); - return ValueTask.CompletedTask; - } + return ValueTask.CompletedTask; } +} - public class PagerShapes : IShapeAttributeProvider +public class PagerShapes : IShapeAttributeProvider +{ + protected readonly IStringLocalizer S; + + public PagerShapes(IStringLocalizer localizer) { - protected readonly IStringLocalizer S; + S = localizer; + } - public PagerShapes(IStringLocalizer localizer) - { - S = localizer; - } + [Shape] + public async Task Pager_Links(Shape shape, DisplayContext displayContext, IShapeFactory shapeFactory, IHtmlHelper Html, + string PagerId, + int Page, + int PageSize, + double TotalItemCount, + int? Quantity, + object FirstText, + object PreviousText, + object NextText, + object LastText, + object GapText, + bool ShowNext, + Dictionary UrlParams) + { + var noFollow = shape.Attributes.ContainsKey("rel") && shape.Attributes["rel"] == "no-follow"; + var currentPage = Page; + if (currentPage < 1) + currentPage = 1; - [Shape] - public async Task Pager_Links(Shape shape, DisplayContext displayContext, IShapeFactory shapeFactory, IHtmlHelper Html, - string PagerId, - int Page, - int PageSize, - double TotalItemCount, - int? Quantity, - object FirstText, - object PreviousText, - object NextText, - object LastText, - object GapText, - bool ShowNext, - Dictionary UrlParams) - { - var noFollow = shape.Attributes.ContainsKey("rel") && shape.Attributes["rel"] == "no-follow"; - var currentPage = Page; - if (currentPage < 1) - currentPage = 1; + var pageSize = PageSize; - var pageSize = PageSize; + var numberOfPagesToShow = Quantity ?? 0; + if (Quantity == null || Quantity < 0) + numberOfPagesToShow = 7; - var numberOfPagesToShow = Quantity ?? 0; - if (Quantity == null || Quantity < 0) - numberOfPagesToShow = 7; + var totalPageCount = pageSize > 0 ? (int)Math.Ceiling(TotalItemCount / pageSize) : 1; - var totalPageCount = pageSize > 0 ? (int)Math.Ceiling(TotalItemCount / pageSize) : 1; + // return shape early if pager is not needed. + if (totalPageCount < 2) + { + shape.Metadata.Type = "List"; + return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } - // return shape early if pager is not needed. - if (totalPageCount < 2) - { - shape.Metadata.Type = "List"; - return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); - } + var firstText = FirstText ?? S["<<"]; + var previousText = PreviousText ?? S["<"]; + var nextText = NextText ?? S[">"]; + var lastText = LastText ?? S[">>"]; + var gapText = GapText ?? S["..."]; - var firstText = FirstText ?? S["<<"]; - var previousText = PreviousText ?? S["<"]; - var nextText = NextText ?? S[">"]; - var lastText = LastText ?? S[">>"]; - var gapText = GapText ?? S["..."]; + var routeData = GetRouteData(shape, displayContext, Html); + SetCustomUrlParams(UrlParams, routeData); - var routeData = GetRouteData(shape, displayContext, Html); - SetCustomUrlParams(UrlParams, routeData); + var firstPage = Math.Max(1, Page - (numberOfPagesToShow / 2)); + var lastPage = Math.Min(totalPageCount, Page + (numberOfPagesToShow / 2)); - var firstPage = Math.Max(1, Page - (numberOfPagesToShow / 2)); - var lastPage = Math.Min(totalPageCount, Page + (numberOfPagesToShow / 2)); + var pageKey = string.IsNullOrEmpty(PagerId) ? "pagenum" : PagerId; - var pageKey = string.IsNullOrEmpty(PagerId) ? "pagenum" : PagerId; + shape.Classes.Add("pager"); + shape.Classes.Add("pagination"); + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "List"; - shape.Classes.Add("pager"); - shape.Classes.Add("pagination"); - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "List"; + // first and previous pages + if ((Page > 1) && (routeData.ContainsKey(pageKey))) + { + routeData.Remove(pageKey); // to keep from having "pagenum=1" in the query string + } - // first and previous pages - if ((Page > 1) && (routeData.ContainsKey(pageKey))) - { - routeData.Remove(pageKey); // to keep from having "pagenum=1" in the query string - } + // first + var firstItem = await shapeFactory.CreateAsync("Pager_First", Arguments.From(new + { + Value = firstText, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape, + Disabled = Page < 2 + })); - // first - var firstItem = await shapeFactory.CreateAsync("Pager_First", Arguments.From(new - { - Value = firstText, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape, - Disabled = Page < 2 - })); + if (noFollow) + { + firstItem.Attributes["rel"] = "no-follow"; + } - if (noFollow) - { - firstItem.Attributes["rel"] = "no-follow"; - } + await shape.AddAsync(firstItem); - await shape.AddAsync(firstItem); + // previous + if ((Page > 1) && (currentPage > 2)) + { // also to keep from having "pagenum=1" in the query string + routeData[pageKey] = currentPage - 1; + } - // previous - if ((Page > 1) && (currentPage > 2)) - { // also to keep from having "pagenum=1" in the query string - routeData[pageKey] = currentPage - 1; - } + var previousItem = await shapeFactory.CreateAsync("Pager_Previous", Arguments.From(new + { + Value = previousText, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape, + Disabled = Page < 2 + })); - var previousItem = await shapeFactory.CreateAsync("Pager_Previous", Arguments.From(new - { - Value = previousText, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape, - Disabled = Page < 2 - })); + if (noFollow) + { + previousItem.Attributes["rel"] = "no-follow"; + } - if (noFollow) - { - previousItem.Attributes["rel"] = "no-follow"; - } + await shape.AddAsync(previousItem); - await shape.AddAsync(previousItem); + // gap at the beginning of the pager + if (firstPage > 1 && numberOfPagesToShow > 0) + { + await shape.AddAsync(await shapeFactory.CreateAsync("Pager_Gap", Arguments.From(new + { + Value = gapText, + Pager = shape + }))); + } - // gap at the beginning of the pager - if (firstPage > 1 && numberOfPagesToShow > 0) + // page numbers + if (numberOfPagesToShow > 0 && lastPage > 1) + { + for (var p = firstPage; p <= lastPage; p++) { - await shape.AddAsync(await shapeFactory.CreateAsync("Pager_Gap", Arguments.From(new + if (p == 1) { - Value = gapText, - Pager = shape - }))); - } + // to keep from having "pagenum=1" in the query string + routeData.Remove(pageKey); + } + else + { + routeData[pageKey] = p; + } - // page numbers - if (numberOfPagesToShow > 0 && lastPage > 1) - { - for (var p = firstPage; p <= lastPage; p++) + if (p == currentPage) { - if (p == 1) + var currentPageItem = await shapeFactory.CreateAsync("Pager_CurrentPage", Arguments.From(new { - // to keep from having "pagenum=1" in the query string - routeData.Remove(pageKey); - } - else + Value = p, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape + })); + + if (noFollow) { - routeData[pageKey] = p; + currentPageItem.Attributes["rel"] = "no-follow"; } - if (p == currentPage) + await shape.AddAsync(currentPageItem); + } + else + { + var pagerItem = await shapeFactory.CreateAsync("Pager_Link", Arguments.From(new + { + Value = p, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape + })); + + if (p > currentPage) { - var currentPageItem = await shapeFactory.CreateAsync("Pager_CurrentPage", Arguments.From(new - { - Value = p, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape - })); - - if (noFollow) - { - currentPageItem.Attributes["rel"] = "no-follow"; - } - - await shape.AddAsync(currentPageItem); + pagerItem.Attributes["rel"] = noFollow ? "no-follow" : "next"; } - else + else if (p < currentPage) { - var pagerItem = await shapeFactory.CreateAsync("Pager_Link", Arguments.From(new - { - Value = p, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape - })); - - if (p > currentPage) - { - pagerItem.Attributes["rel"] = noFollow ? "no-follow" : "next"; - } - else if (p < currentPage) - { - pagerItem.Attributes["rel"] = noFollow ? "no-follow" : "prev"; - } - - await shape.AddAsync(pagerItem); + pagerItem.Attributes["rel"] = noFollow ? "no-follow" : "prev"; } + + await shape.AddAsync(pagerItem); } } + } - // gap at the end of the pager - if (lastPage < totalPageCount && numberOfPagesToShow > 0) + // gap at the end of the pager + if (lastPage < totalPageCount && numberOfPagesToShow > 0) + { + await shape.AddAsync(await shapeFactory.CreateAsync("Pager_Gap", Arguments.From(new { - await shape.AddAsync(await shapeFactory.CreateAsync("Pager_Gap", Arguments.From(new - { - Value = gapText, - Pager = shape - }))); - } + Value = gapText, + Pager = shape + }))); + } - // Next - routeData[pageKey] = Page + 1; - var pagerNextItem = await shapeFactory.CreateAsync("Pager_Next", Arguments.From(new - { - Value = nextText, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape, - Disabled = Page >= totalPageCount && !ShowNext - })); + // Next + routeData[pageKey] = Page + 1; + var pagerNextItem = await shapeFactory.CreateAsync("Pager_Next", Arguments.From(new + { + Value = nextText, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape, + Disabled = Page >= totalPageCount && !ShowNext + })); - if (noFollow) - { - pagerNextItem.Attributes["rel"] = "no-follow"; - } + if (noFollow) + { + pagerNextItem.Attributes["rel"] = "no-follow"; + } - await shape.AddAsync(pagerNextItem); + await shape.AddAsync(pagerNextItem); - // Last - routeData[pageKey] = totalPageCount; - var pagerLastItem = await shapeFactory.CreateAsync("Pager_Last", Arguments.From(new - { - Value = lastText, - RouteValues = new RouteValueDictionary(routeData), - Pager = shape, - Disabled = Page >= totalPageCount - })); + // Last + routeData[pageKey] = totalPageCount; + var pagerLastItem = await shapeFactory.CreateAsync("Pager_Last", Arguments.From(new + { + Value = lastText, + RouteValues = new RouteValueDictionary(routeData), + Pager = shape, + Disabled = Page >= totalPageCount + })); - if (noFollow) - { - pagerLastItem.Attributes["rel"] = "no-follow"; - } + if (noFollow) + { + pagerLastItem.Attributes["rel"] = "no-follow"; + } - await shape.AddAsync(pagerLastItem); + await shape.AddAsync(pagerLastItem); - return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); - } + return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } - [Shape] + [Shape] #pragma warning disable CA1822 // Mark members as static - public Task Pager(Shape shape, DisplayContext displayContext) - { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Links"; - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); - } + public Task Pager(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Links"; + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } - [Shape] - public async Task PagerSlim(Shape shape, DisplayContext displayContext, IShapeFactory shapeFactory, IHtmlHelper Html, - object PreviousText, - object NextText, - string PreviousClass, - string NextClass, - Dictionary UrlParams) - { - var noFollow = shape.Attributes.ContainsKey("rel") && shape.Attributes["rel"] == "no-follow"; - var previousText = PreviousText ?? S["<"]; - var nextText = NextText ?? S[">"]; + [Shape] + public async Task PagerSlim(Shape shape, DisplayContext displayContext, IShapeFactory shapeFactory, IHtmlHelper Html, + object PreviousText, + object NextText, + string PreviousClass, + string NextClass, + Dictionary UrlParams) + { + var noFollow = shape.Attributes.ContainsKey("rel") && shape.Attributes["rel"] == "no-follow"; + var previousText = PreviousText ?? S["<"]; + var nextText = NextText ?? S[">"]; - shape.Classes.Add("pager"); - shape.Classes.Add("pagination"); - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "List"; + shape.Classes.Add("pager"); + shape.Classes.Add("pagination"); + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "List"; - var routeData = GetRouteData(shape, displayContext, Html); - SetCustomUrlParams(UrlParams, routeData); + var routeData = GetRouteData(shape, displayContext, Html); + SetCustomUrlParams(UrlParams, routeData); - if (shape.TryGetProperty("Before", out string before)) + if (shape.TryGetProperty("Before", out string before)) + { + var beforeRouteData = new RouteValueDictionary(routeData) { - var beforeRouteData = new RouteValueDictionary(routeData) - { - ["before"] = before - }; + ["before"] = before + }; - beforeRouteData.Remove("after"); + beforeRouteData.Remove("after"); - var previousItem = await shapeFactory.CreateAsync("Pager_Previous", Arguments.From(new - { - Value = previousText, - RouteValues = beforeRouteData, - Pager = shape - })); - - if (noFollow) - { - previousItem.Attributes["rel"] = "no-follow"; - } + var previousItem = await shapeFactory.CreateAsync("Pager_Previous", Arguments.From(new + { + Value = previousText, + RouteValues = beforeRouteData, + Pager = shape + })); - await shape.AddAsync(previousItem); - shape.Properties["FirstClass"] = PreviousClass; - } - else + if (noFollow) { - routeData.Remove("before"); + previousItem.Attributes["rel"] = "no-follow"; } - if (shape.TryGetProperty("After", out string after)) - { - var afterRouteData = new RouteValueDictionary(routeData) - { - ["after"] = after - }; + await shape.AddAsync(previousItem); + shape.Properties["FirstClass"] = PreviousClass; + } + else + { + routeData.Remove("before"); + } - afterRouteData.Remove("before"); + if (shape.TryGetProperty("After", out string after)) + { + var afterRouteData = new RouteValueDictionary(routeData) + { + ["after"] = after + }; - var nextItem = await shapeFactory.CreateAsync("Pager_Next", Arguments.From(new - { - Value = nextText, - RouteValues = afterRouteData, - Pager = shape - })); + afterRouteData.Remove("before"); - if (noFollow) - { - nextItem.Attributes["rel"] = "no-follow"; - } + var nextItem = await shapeFactory.CreateAsync("Pager_Next", Arguments.From(new + { + Value = nextText, + RouteValues = afterRouteData, + Pager = shape + })); - await shape.AddAsync(nextItem); - shape.Properties["LastClass"] = NextClass; - } - else + if (noFollow) { - routeData.Remove("after"); + nextItem.Attributes["rel"] = "no-follow"; } - return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); + await shape.AddAsync(nextItem); + shape.Properties["LastClass"] = NextClass; } - - [Shape] - public Task Pager_First(Shape shape, DisplayContext displayContext) + else { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + routeData.Remove("after"); } - [Shape] - public Task Pager_Previous(Shape shape, DisplayContext displayContext) - { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; + return await displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } - if (!shape.Attributes.ContainsKey("rel")) - { - shape.Attributes["rel"] = "prev"; - } + [Shape] + public Task Pager_First(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); - } + [Shape] + public Task Pager_Previous(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; - [Shape] - public Task Pager_CurrentPage(Shape shape, DisplayContext displayContext) + if (!shape.Attributes.ContainsKey("rel")) { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; - var parentTag = shape.GetProperty("Tag"); - parentTag.AddCssClass("active"); - - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + shape.Attributes["rel"] = "prev"; } - [Shape] - public Task Pager_Next(Shape shape, DisplayContext displayContext) + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } + + [Shape] + public Task Pager_CurrentPage(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; + var parentTag = shape.GetProperty("Tag"); + parentTag.AddCssClass("active"); + + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } + + [Shape] + public Task Pager_Next(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; + + if (!shape.Attributes.ContainsKey("rel")) { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; + shape.Attributes["rel"] = "next"; + } - if (!shape.Attributes.ContainsKey("rel")) + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } + + [Shape] + public Task Pager_Last(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } + + [Shape] + public Task Pager_Link(Shape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "ActionLink"; + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } + + [Shape] + public IHtmlContent ActionLink(Shape shape, IUrlHelper Url, object Value, bool Disabled = false) + { + if (Disabled) + { + if (shape.TryGetProperty("Tag", out TagBuilder tagBuilder)) { - shape.Attributes["rel"] = "next"; + tagBuilder.AddCssClass("disabled"); } - - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); } - [Shape] - public Task Pager_Last(Shape shape, DisplayContext displayContext) + var routeValues = shape.GetProperty("RouteValues") ?? []; + if (!Disabled) { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + shape.Attributes["href"] = Url.Action((string)routeValues["action"], (string)routeValues["controller"], routeValues); } - - [Shape] - public Task Pager_Link(Shape shape, DisplayContext displayContext) + else { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "ActionLink"; - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + shape.Attributes.Remove("href"); } - [Shape] - public IHtmlContent ActionLink(Shape shape, IUrlHelper Url, object Value, bool Disabled = false) - { - if (Disabled) - { - if (shape.TryGetProperty("Tag", out TagBuilder tagBuilder)) - { - tagBuilder.AddCssClass("disabled"); - } - } + var tag = shape.GetTagBuilder("a"); - var routeValues = shape.GetProperty("RouteValues") ?? []; - if (!Disabled) - { - shape.Attributes["href"] = Url.Action((string)routeValues["action"], (string)routeValues["controller"], routeValues); - } - else - { - shape.Attributes.Remove("href"); - } + tag.InnerHtml.AppendHtml(CoerceHtmlString(Value)); + return tag; + } - var tag = shape.GetTagBuilder("a"); + [Shape] + public Task Pager_Gap(IShape shape, DisplayContext displayContext) + { + shape.Metadata.Alternates.Clear(); + shape.Metadata.Type = "Pager_Link"; + var parentTag = shape.GetProperty("Tag"); + parentTag.AddCssClass("disabled"); + return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + } +#pragma warning restore CA1822 // Mark members as static - tag.InnerHtml.AppendHtml(CoerceHtmlString(Value)); - return tag; + private static IHtmlContent CoerceHtmlString(object value) + { + if (value == null) + { + return HtmlString.Empty; } - [Shape] - public Task Pager_Gap(IShape shape, DisplayContext displayContext) + if (value is IHtmlContent result) { - shape.Metadata.Alternates.Clear(); - shape.Metadata.Type = "Pager_Link"; - var parentTag = shape.GetProperty("Tag"); - parentTag.AddCssClass("disabled"); - return displayContext.DisplayHelper.ShapeExecuteAsync(shape); + return result; } -#pragma warning restore CA1822 // Mark members as static - private static IHtmlContent CoerceHtmlString(object value) - { - if (value == null) - { - return HtmlString.Empty; - } + return new HtmlContentString(value.ToString()); + } - if (value is IHtmlContent result) - { - return result; - } + private static RouteValueDictionary GetRouteData(Shape shape, DisplayContext displayContext, IHtmlHelper Html) + { + var httpContextAccessor = displayContext.ServiceProvider.GetService(); + var httpContext = httpContextAccessor.HttpContext; - return new HtmlContentString(value.ToString()); - } + var routeData = new RouteValueDictionary(Html.ViewContext.RouteData.Values); - private static RouteValueDictionary GetRouteData(Shape shape, DisplayContext displayContext, IHtmlHelper Html) + if (httpContext != null) { - var httpContextAccessor = displayContext.ServiceProvider.GetService(); - var httpContext = httpContextAccessor.HttpContext; + var query = httpContext.Request.Query; - var routeData = new RouteValueDictionary(Html.ViewContext.RouteData.Values); - - if (httpContext != null) + foreach (var key in query.Keys) { - var query = httpContext.Request.Query; - - foreach (var key in query.Keys) - { - routeData.TryAdd(key, query[key]); - } + routeData.TryAdd(key, query[key]); } + } - // specific cross-requests route data can be passed to the shape directly (e.g., OrchardCore.Users) - var shapeRouteData = shape.GetProperty("RouteData"); - if (shapeRouteData != null) + // specific cross-requests route data can be passed to the shape directly (e.g., OrchardCore.Users) + var shapeRouteData = shape.GetProperty("RouteData"); + if (shapeRouteData != null) + { + foreach (var rd in shapeRouteData.Values) { - foreach (var rd in shapeRouteData.Values) - { - routeData[rd.Key] = rd.Value; - } + routeData[rd.Key] = rd.Value; } - - return routeData; } - private static void SetCustomUrlParams(Dictionary urlParams, RouteValueDictionary routeData) + return routeData; + } + + private static void SetCustomUrlParams(Dictionary urlParams, RouteValueDictionary routeData) + { + // Allows to pass custom url params to Pager + if (urlParams != null) { - // Allows to pass custom url params to Pager - if (urlParams != null) + foreach (var item in urlParams) { - foreach (var item in urlParams) - { - routeData[item.Key] = item.Value; - } + routeData[item.Key] = item.Value; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Navigation/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Navigation/Startup.cs index cf00bbb1672..357588400d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Navigation/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Navigation/Startup.cs @@ -3,27 +3,26 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; -namespace OrchardCore.Navigation +namespace OrchardCore.Navigation; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - private readonly IShellConfiguration _shellConfiguration; + private readonly IShellConfiguration _shellConfiguration; - public Startup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + public Startup(IShellConfiguration shellConfiguration) + { + _shellConfiguration = shellConfiguration; + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddNavigation(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddNavigation(); - services.AddScoped(); - services.AddScoped(); - services.AddShapeAttributes(); + services.AddScoped(); + services.AddScoped(); + services.AddShapeAttributes(); - var navigationConfiguration = _shellConfiguration.GetSection("OrchardCore_Navigation"); - services.Configure(navigationConfiguration.GetSection("PagerOptions")); - } + var navigationConfiguration = _shellConfiguration.GetSection("OrchardCore_Navigation"); + services.Configure(navigationConfiguration.GetSection("PagerOptions")); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/AdminMenu.cs index 600c10fd192..5a725c5f49b 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/AdminMenu.cs @@ -6,91 +6,90 @@ using OrchardCore.Environment.Shell.Descriptor.Models; using OrchardCore.Navigation; -namespace OrchardCore.OpenId +namespace OrchardCore.OpenId; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _clientRouteValues = new() { - private static readonly RouteValueDictionary _clientRouteValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", "OrchardCore.OpenId.Client" }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", "OrchardCore.OpenId.Client" }, + }; + + private readonly ShellDescriptor _shellDescriptor; - private readonly ShellDescriptor _shellDescriptor; + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public AdminMenu( + IStringLocalizer localizer, + ShellDescriptor shellDescriptor) + { + S = localizer; + _shellDescriptor = shellDescriptor; + } - public AdminMenu( - IStringLocalizer localizer, - ShellDescriptor shellDescriptor) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; - _shellDescriptor = shellDescriptor; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + builder.Add(S["Security"], security => security + .Add(S["OpenID Connect"], S["OpenID Connect"].PrefixPosition(), category => { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + category.AddClass("openid").Id("openid"); - builder.Add(S["Security"], security => security - .Add(S["OpenID Connect"], S["OpenID Connect"].PrefixPosition(), category => + var features = _shellDescriptor.Features.Select(feature => feature.Id).ToImmutableArray(); + if (features.Contains(OpenIdConstants.Features.Client) || + features.Contains(OpenIdConstants.Features.Server) || + features.Contains(OpenIdConstants.Features.Validation)) { - category.AddClass("openid").Id("openid"); - - var features = _shellDescriptor.Features.Select(feature => feature.Id).ToImmutableArray(); - if (features.Contains(OpenIdConstants.Features.Client) || - features.Contains(OpenIdConstants.Features.Server) || - features.Contains(OpenIdConstants.Features.Validation)) + category.Add(S["Settings"], "1", settings => { - category.Add(S["Settings"], "1", settings => + if (features.Contains(OpenIdConstants.Features.Client)) { - if (features.Contains(OpenIdConstants.Features.Client)) - { - settings.Add(S["Authentication client"], "1", client => client - .Action("Index", "Admin", _clientRouteValues) - .Permission(Permissions.ManageClientSettings) - .LocalNav()); - } + settings.Add(S["Authentication client"], "1", client => client + .Action("Index", "Admin", _clientRouteValues) + .Permission(Permissions.ManageClientSettings) + .LocalNav()); + } - if (features.Contains(OpenIdConstants.Features.Server)) - { - settings.Add(S["Authorization server"], "2", server => server - .Action("Index", "ServerConfiguration", "OrchardCore.OpenId") - .Permission(Permissions.ManageServerSettings) - .LocalNav()); - } + if (features.Contains(OpenIdConstants.Features.Server)) + { + settings.Add(S["Authorization server"], "2", server => server + .Action("Index", "ServerConfiguration", "OrchardCore.OpenId") + .Permission(Permissions.ManageServerSettings) + .LocalNav()); + } - if (features.Contains(OpenIdConstants.Features.Validation)) - { - settings.Add(S["Token validation"], "3", validation => validation - .Action("Index", "ValidationConfiguration", "OrchardCore.OpenId") - .Permission(Permissions.ManageValidationSettings) - .LocalNav()); - } - }); - } + if (features.Contains(OpenIdConstants.Features.Validation)) + { + settings.Add(S["Token validation"], "3", validation => validation + .Action("Index", "ValidationConfiguration", "OrchardCore.OpenId") + .Permission(Permissions.ManageValidationSettings) + .LocalNav()); + } + }); + } - if (features.Contains(OpenIdConstants.Features.Management)) + if (features.Contains(OpenIdConstants.Features.Management)) + { + category.Add(S["Management"], "2", management => { - category.Add(S["Management"], "2", management => - { - management.Add(S["Applications"], "1", applications => applications - .Action("Index", "Application", "OrchardCore.OpenId") - .Permission(Permissions.ManageApplications) - .LocalNav()); + management.Add(S["Applications"], "1", applications => applications + .Action("Index", "Application", "OrchardCore.OpenId") + .Permission(Permissions.ManageApplications) + .LocalNav()); - management.Add(S["Scopes"], "2", applications => applications - .Action("Index", "Scope", "OrchardCore.OpenId") - .Permission(Permissions.ManageScopes) - .LocalNav()); - }); - } - })); + management.Add(S["Scopes"], "2", applications => applications + .Action("Index", "Scope", "OrchardCore.OpenId") + .Permission(Permissions.ManageScopes) + .LocalNav()); + }); + } + })); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdClientConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdClientConfiguration.cs index 53cb8d059b5..d15be9b3825 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdClientConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdClientConfiguration.cs @@ -12,129 +12,128 @@ using OrchardCore.OpenId.Services; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Configuration +namespace OrchardCore.OpenId.Configuration; + +public sealed class OpenIdClientConfiguration : + IConfigureOptions, + IConfigureNamedOptions { - public sealed class OpenIdClientConfiguration : - IConfigureOptions, - IConfigureNamedOptions + private readonly IOpenIdClientService _clientService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly ShellSettings _shellSettings; + private readonly ILogger _logger; + + public OpenIdClientConfiguration( + IOpenIdClientService clientService, + IDataProtectionProvider dataProtectionProvider, + ShellSettings shellSettings, + ILogger logger) { - private readonly IOpenIdClientService _clientService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly ShellSettings _shellSettings; - private readonly ILogger _logger; - - public OpenIdClientConfiguration( - IOpenIdClientService clientService, - IDataProtectionProvider dataProtectionProvider, - ShellSettings shellSettings, - ILogger logger) + _clientService = clientService; + _dataProtectionProvider = dataProtectionProvider; + _shellSettings = shellSettings; + _logger = logger; + } + + public void Configure(AuthenticationOptions options) + { + var settings = GetClientSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - _clientService = clientService; - _dataProtectionProvider = dataProtectionProvider; - _shellSettings = shellSettings; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) - { - var settings = GetClientSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) - { - return; - } + // Register the OpenID Connect client handler in the authentication handlers collection. + options.AddScheme(OpenIdConnectDefaults.AuthenticationScheme, settings.DisplayName); + } - // Register the OpenID Connect client handler in the authentication handlers collection. - options.AddScheme(OpenIdConnectDefaults.AuthenticationScheme, settings.DisplayName); + public void Configure(string name, OpenIdConnectOptions options) + { + // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. + if (!string.Equals(name, OpenIdConnectDefaults.AuthenticationScheme, StringComparison.Ordinal)) + { + return; } - public void Configure(string name, OpenIdConnectOptions options) + var settings = GetClientSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - // Ignore OpenID Connect client handler instances that don't correspond to the instance managed by the OpenID module. - if (!string.Equals(name, OpenIdConnectDefaults.AuthenticationScheme, StringComparison.Ordinal)) - { - return; - } + return; + } + + options.Authority = settings.Authority.AbsoluteUri; + options.ClientId = settings.ClientId; + options.SignedOutRedirectUri = settings.SignedOutRedirectUri ?? options.SignedOutRedirectUri; + options.SignedOutCallbackPath = settings.SignedOutCallbackPath ?? options.SignedOutCallbackPath; + options.RequireHttpsMetadata = string.Equals(settings.Authority.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); + options.GetClaimsFromUserInfoEndpoint = true; + options.ResponseMode = settings.ResponseMode; + options.ResponseType = settings.ResponseType; + options.SaveTokens = settings.StoreExternalTokens; + + options.CallbackPath = settings.CallbackPath ?? options.CallbackPath; - var settings = GetClientSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) + if (settings.Scopes != null) + { + foreach (var scope in settings.Scopes) { - return; + options.Scope.Add(scope); } + } - options.Authority = settings.Authority.AbsoluteUri; - options.ClientId = settings.ClientId; - options.SignedOutRedirectUri = settings.SignedOutRedirectUri ?? options.SignedOutRedirectUri; - options.SignedOutCallbackPath = settings.SignedOutCallbackPath ?? options.SignedOutCallbackPath; - options.RequireHttpsMetadata = string.Equals(settings.Authority.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); - options.GetClaimsFromUserInfoEndpoint = true; - options.ResponseMode = settings.ResponseMode; - options.ResponseType = settings.ResponseType; - options.SaveTokens = settings.StoreExternalTokens; - - options.CallbackPath = settings.CallbackPath ?? options.CallbackPath; + if (!string.IsNullOrEmpty(settings.ClientSecret)) + { + var protector = _dataProtectionProvider.CreateProtector(nameof(OpenIdClientConfiguration)); - if (settings.Scopes != null) + try { - foreach (var scope in settings.Scopes) - { - options.Scope.Add(scope); - } + options.ClientSecret = protector.Unprotect(settings.ClientSecret); } - - if (!string.IsNullOrEmpty(settings.ClientSecret)) + catch { - var protector = _dataProtectionProvider.CreateProtector(nameof(OpenIdClientConfiguration)); - - try - { - options.ClientSecret = protector.Unprotect(settings.ClientSecret); - } - catch - { - _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); - } + _logger.LogError("The client secret could not be decrypted. It may have been encrypted using a different key."); } + } - if (settings.Parameters != null && settings.Parameters.Length > 0) + if (settings.Parameters != null && settings.Parameters.Length > 0) + { + var parameters = settings.Parameters; + options.Events.OnRedirectToIdentityProvider = (context) => { - var parameters = settings.Parameters; - options.Events.OnRedirectToIdentityProvider = (context) => + foreach (var parameter in parameters) { - foreach (var parameter in parameters) - { - context.ProtocolMessage.SetParameter(parameter.Name, parameter.Value); - } + context.ProtocolMessage.SetParameter(parameter.Name, parameter.Value); + } - return Task.CompletedTask; - }; - } + return Task.CompletedTask; + }; } + } - public void Configure(OpenIdConnectOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); + public void Configure(OpenIdConnectOptions options) => Debug.Fail("This infrastructure method shouldn't be called."); - private async Task GetClientSettingsAsync() - { - var settings = await _clientService.GetSettingsAsync(); + private async Task GetClientSettingsAsync() + { + var settings = await _clientService.GetSettingsAsync(); - var result = await _clientService.ValidateSettingsAsync(settings); + var result = await _clientService.ValidateSettingsAsync(settings); - if (result.Any(x => x != ValidationResult.Success)) + if (result.Any(x => x != ValidationResult.Success)) + { + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + if (_logger.IsEnabled(LogLevel.Warning)) { - if (_logger.IsEnabled(LogLevel.Warning)) - { - var errors = result.Where(x => x != ValidationResult.Success) - .Select(x => x.ErrorMessage); + var errors = result.Where(x => x != ValidationResult.Success) + .Select(x => x.ErrorMessage); - _logger.LogWarning("The OpenID client settings are invalid: {Errors}", string.Join(System.Environment.NewLine, errors)); - } - - return null; + _logger.LogWarning("The OpenID client settings are invalid: {Errors}", string.Join(System.Environment.NewLine, errors)); } - } - return settings; + return null; + } } + + return settings; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdServerConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdServerConfiguration.cs index 012b928e084..72c2dd89d43 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdServerConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdServerConfiguration.cs @@ -15,239 +15,238 @@ using OrchardCore.OpenId.Settings; using static OpenIddict.Abstractions.OpenIddictConstants; -namespace OrchardCore.OpenId.Configuration +namespace OrchardCore.OpenId.Configuration; + +public sealed class OpenIdServerConfiguration : IConfigureOptions, + IConfigureOptions, + IConfigureOptions, + IConfigureNamedOptions { - public sealed class OpenIdServerConfiguration : IConfigureOptions, - IConfigureOptions, - IConfigureOptions, - IConfigureNamedOptions + private readonly IOpenIdServerService _serverService; + private readonly ShellSettings _shellSettings; + private readonly ILogger _logger; + + public OpenIdServerConfiguration( + IOpenIdServerService serverService, + ShellSettings shellSettings, + ILogger logger) { - private readonly IOpenIdServerService _serverService; - private readonly ShellSettings _shellSettings; - private readonly ILogger _logger; + _serverService = serverService; + _shellSettings = shellSettings; + _logger = logger; + } - public OpenIdServerConfiguration( - IOpenIdServerService serverService, - ShellSettings shellSettings, - ILogger logger) + public void Configure(AuthenticationOptions options) + { + var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - _serverService = serverService; - _shellSettings = shellSettings; - _logger = logger; + return; } - public void Configure(AuthenticationOptions options) - { - var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) - { - return; - } - - options.AddScheme( - OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, displayName: null); - } + options.AddScheme( + OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, displayName: null); + } - public void Configure(OpenIddictServerOptions options) + public void Configure(OpenIddictServerOptions options) + { + var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) - { - return; - } + return; + } - options.Issuer = settings.Authority; - options.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption; - options.DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens; - options.UseReferenceAccessTokens = settings.UseReferenceAccessTokens; + options.Issuer = settings.Authority; + options.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption; + options.DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens; + options.UseReferenceAccessTokens = settings.UseReferenceAccessTokens; - foreach (var key in _serverService.GetEncryptionKeysAsync().GetAwaiter().GetResult()) - { - options.EncryptionCredentials.Add(new EncryptingCredentials(key, - SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)); - } + foreach (var key in _serverService.GetEncryptionKeysAsync().GetAwaiter().GetResult()) + { + options.EncryptionCredentials.Add(new EncryptingCredentials(key, + SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)); + } - foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult()) - { - options.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256)); - } + foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult()) + { + options.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256)); + } - // Note: while endpoint paths in OrchardCore are stored as PathString instances, - // OpenIddict uses System.Uri. To ensure the System.Uri instances created from - // a PathString don't represent root-relative URIs (which would break path-based - // multi-tenancy support), the leading '/' that is always present in PathString - // instances is manually removed from the endpoint path before URIs are created. + // Note: while endpoint paths in OrchardCore are stored as PathString instances, + // OpenIddict uses System.Uri. To ensure the System.Uri instances created from + // a PathString don't represent root-relative URIs (which would break path-based + // multi-tenancy support), the leading '/' that is always present in PathString + // instances is manually removed from the endpoint path before URIs are created. - if (settings.AuthorizationEndpointPath.HasValue) - { - options.AuthorizationEndpointUris.Add(new Uri( - settings.AuthorizationEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.AuthorizationEndpointPath.HasValue) + { + options.AuthorizationEndpointUris.Add(new Uri( + settings.AuthorizationEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - if (settings.LogoutEndpointPath.HasValue) - { - options.LogoutEndpointUris.Add(new Uri( - settings.LogoutEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.LogoutEndpointPath.HasValue) + { + options.LogoutEndpointUris.Add(new Uri( + settings.LogoutEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - if (settings.TokenEndpointPath.HasValue) - { - options.TokenEndpointUris.Add(new Uri( - settings.TokenEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.TokenEndpointPath.HasValue) + { + options.TokenEndpointUris.Add(new Uri( + settings.TokenEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - if (settings.UserinfoEndpointPath.HasValue) - { - options.UserinfoEndpointUris.Add(new Uri( - settings.UserinfoEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.UserinfoEndpointPath.HasValue) + { + options.UserinfoEndpointUris.Add(new Uri( + settings.UserinfoEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - if (settings.IntrospectionEndpointPath.HasValue) - { - options.IntrospectionEndpointUris.Add(new Uri( - settings.IntrospectionEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.IntrospectionEndpointPath.HasValue) + { + options.IntrospectionEndpointUris.Add(new Uri( + settings.IntrospectionEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - if (settings.RevocationEndpointPath.HasValue) - { - options.RevocationEndpointUris.Add(new Uri( - settings.RevocationEndpointPath.ToUriComponent()[1..], UriKind.Relative)); - } + if (settings.RevocationEndpointPath.HasValue) + { + options.RevocationEndpointUris.Add(new Uri( + settings.RevocationEndpointPath.ToUriComponent()[1..], UriKind.Relative)); + } - // For now, response types and response modes are not directly - // configurable and are inferred from the selected flows. - if (settings.AllowAuthorizationCodeFlow) - { - options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain); - options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256); + // For now, response types and response modes are not directly + // configurable and are inferred from the selected flows. + if (settings.AllowAuthorizationCodeFlow) + { + options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain); + options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256); - options.GrantTypes.Add(GrantTypes.AuthorizationCode); + options.GrantTypes.Add(GrantTypes.AuthorizationCode); - options.ResponseModes.Add(ResponseModes.FormPost); - options.ResponseModes.Add(ResponseModes.Fragment); - options.ResponseModes.Add(ResponseModes.Query); + options.ResponseModes.Add(ResponseModes.FormPost); + options.ResponseModes.Add(ResponseModes.Fragment); + options.ResponseModes.Add(ResponseModes.Query); - options.ResponseTypes.Add(ResponseTypes.Code); - } + options.ResponseTypes.Add(ResponseTypes.Code); + } - if (settings.AllowClientCredentialsFlow) - { - options.GrantTypes.Add(GrantTypes.ClientCredentials); - } + if (settings.AllowClientCredentialsFlow) + { + options.GrantTypes.Add(GrantTypes.ClientCredentials); + } - if (settings.AllowHybridFlow) - { - options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain); - options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256); + if (settings.AllowHybridFlow) + { + options.CodeChallengeMethods.Add(CodeChallengeMethods.Plain); + options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256); - options.GrantTypes.Add(GrantTypes.AuthorizationCode); - options.GrantTypes.Add(GrantTypes.Implicit); + options.GrantTypes.Add(GrantTypes.AuthorizationCode); + options.GrantTypes.Add(GrantTypes.Implicit); - options.ResponseModes.Add(ResponseModes.FormPost); - options.ResponseModes.Add(ResponseModes.Fragment); + options.ResponseModes.Add(ResponseModes.FormPost); + options.ResponseModes.Add(ResponseModes.Fragment); - options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken); - options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken + ' ' + ResponseTypes.Token); - options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.Token); - } + options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken); + options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken + ' ' + ResponseTypes.Token); + options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.Token); + } - if (settings.AllowImplicitFlow) - { - options.GrantTypes.Add(GrantTypes.Implicit); + if (settings.AllowImplicitFlow) + { + options.GrantTypes.Add(GrantTypes.Implicit); - options.ResponseModes.Add(ResponseModes.FormPost); - options.ResponseModes.Add(ResponseModes.Fragment); + options.ResponseModes.Add(ResponseModes.FormPost); + options.ResponseModes.Add(ResponseModes.Fragment); - options.ResponseTypes.Add(ResponseTypes.IdToken); - options.ResponseTypes.Add(ResponseTypes.IdToken + ' ' + ResponseTypes.Token); - options.ResponseTypes.Add(ResponseTypes.Token); - } + options.ResponseTypes.Add(ResponseTypes.IdToken); + options.ResponseTypes.Add(ResponseTypes.IdToken + ' ' + ResponseTypes.Token); + options.ResponseTypes.Add(ResponseTypes.Token); + } - if (settings.AllowPasswordFlow) - { - options.GrantTypes.Add(GrantTypes.Password); - } + if (settings.AllowPasswordFlow) + { + options.GrantTypes.Add(GrantTypes.Password); + } - if (settings.AllowRefreshTokenFlow) - { - options.GrantTypes.Add(GrantTypes.RefreshToken); + if (settings.AllowRefreshTokenFlow) + { + options.GrantTypes.Add(GrantTypes.RefreshToken); - options.Scopes.Add(Scopes.OfflineAccess); - } + options.Scopes.Add(Scopes.OfflineAccess); + } - options.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange; + options.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange; - options.Scopes.Add(Scopes.Email); - options.Scopes.Add(Scopes.Phone); - options.Scopes.Add(Scopes.Profile); - options.Scopes.Add(Scopes.Roles); - } + options.Scopes.Add(Scopes.Email); + options.Scopes.Add(Scopes.Phone); + options.Scopes.Add(Scopes.Profile); + options.Scopes.Add(Scopes.Roles); + } - public void Configure(OpenIddictServerDataProtectionOptions options) + public void Configure(OpenIddictServerDataProtectionOptions options) + { + var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - var settings = GetServerSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) - { - return; - } - - // All the tokens produced by the server feature use ASP.NET Core Data Protection as the default - // token format, but an option is provided to allow switching to JWT for access tokens only. - options.PreferDefaultAccessTokenFormat = settings.AccessTokenFormat == OpenIdServerSettings.TokenFormat.JsonWebToken; + return; } - public void Configure(string name, OpenIddictServerAspNetCoreOptions options) - { - // Note: the OpenID module handles the authorization, logout, token and userinfo requests - // in its dedicated ASP.NET Core MVC controller, which requires enabling the pass-through mode. - options.EnableAuthorizationEndpointPassthrough = true; - options.EnableLogoutEndpointPassthrough = true; - options.EnableTokenEndpointPassthrough = true; - options.EnableUserinfoEndpointPassthrough = true; + // All the tokens produced by the server feature use ASP.NET Core Data Protection as the default + // token format, but an option is provided to allow switching to JWT for access tokens only. + options.PreferDefaultAccessTokenFormat = settings.AccessTokenFormat == OpenIdServerSettings.TokenFormat.JsonWebToken; + } - // Note: caching is enabled for both authorization and logout requests to allow sending - // large POST authorization and logout requests, but can be programmatically disabled, as the - // authorization and logout views support flowing the entire payload and not just the request_id. - options.EnableAuthorizationRequestCaching = true; - options.EnableLogoutRequestCaching = true; + public void Configure(string name, OpenIddictServerAspNetCoreOptions options) + { + // Note: the OpenID module handles the authorization, logout, token and userinfo requests + // in its dedicated ASP.NET Core MVC controller, which requires enabling the pass-through mode. + options.EnableAuthorizationEndpointPassthrough = true; + options.EnableLogoutEndpointPassthrough = true; + options.EnableTokenEndpointPassthrough = true; + options.EnableUserinfoEndpointPassthrough = true; + + // Note: caching is enabled for both authorization and logout requests to allow sending + // large POST authorization and logout requests, but can be programmatically disabled, as the + // authorization and logout views support flowing the entire payload and not just the request_id. + options.EnableAuthorizationRequestCaching = true; + options.EnableLogoutRequestCaching = true; + + // Note: error pass-through is enabled to allow the actions of the MVC authorization controller + // to handle the errors returned by the interactive endpoints without relying on the generic + // status code pages middleware to rewrite the response later in the request processing. + options.EnableErrorPassthrough = true; + + // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module. + // To make configuration easier and avoid having to configure it in two different features, + // the transport security requirement enforced by OpenIddict by default is always turned off. + options.DisableTransportSecurityRequirement = true; + } - // Note: error pass-through is enabled to allow the actions of the MVC authorization controller - // to handle the errors returned by the interactive endpoints without relying on the generic - // status code pages middleware to rewrite the response later in the request processing. - options.EnableErrorPassthrough = true; + public void Configure(OpenIddictServerAspNetCoreOptions options) + => Debug.Fail("This infrastructure method shouldn't be called."); - // Note: in Orchard, transport security is usually configured via the dedicated HTTPS module. - // To make configuration easier and avoid having to configure it in two different features, - // the transport security requirement enforced by OpenIddict by default is always turned off. - options.DisableTransportSecurityRequirement = true; - } + private async Task GetServerSettingsAsync() + { + var settings = await _serverService.GetSettingsAsync(); - public void Configure(OpenIddictServerAspNetCoreOptions options) - => Debug.Fail("This infrastructure method shouldn't be called."); + var result = await _serverService.ValidateSettingsAsync(settings); - private async Task GetServerSettingsAsync() + if (result.Any(result => result != ValidationResult.Success)) { - var settings = await _serverService.GetSettingsAsync(); - - var result = await _serverService.ValidateSettingsAsync(settings); - - if (result.Any(result => result != ValidationResult.Success)) + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + if (_logger.IsEnabled(LogLevel.Warning)) { - if (_logger.IsEnabled(LogLevel.Warning)) - { - var errors = result.Where(x => x != ValidationResult.Success) - .Select(x => x.ErrorMessage); - - _logger.LogWarning("The OpenID server settings are invalid: {Errors}", string.Join(System.Environment.NewLine, errors)); - } + var errors = result.Where(x => x != ValidationResult.Success) + .Select(x => x.ErrorMessage); - return null; + _logger.LogWarning("The OpenID server settings are invalid: {Errors}", string.Join(System.Environment.NewLine, errors)); } - } - return settings; + return null; + } } + + return settings; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs index b6cbf6bff86..6098f770a0e 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Configuration/OpenIdValidationConfiguration.cs @@ -22,274 +22,273 @@ using static OpenIddict.Abstractions.OpenIddictConstants; using SystemEnvironment = System.Environment; -namespace OrchardCore.OpenId.Configuration +namespace OrchardCore.OpenId.Configuration; + +public sealed class OpenIdValidationConfiguration : IConfigureOptions, + IConfigureOptions, + IConfigureOptions, + IConfigureNamedOptions { - public sealed class OpenIdValidationConfiguration : IConfigureOptions, - IConfigureOptions, - IConfigureOptions, - IConfigureNamedOptions + private readonly ILogger _logger; + private readonly IOpenIdValidationService _validationService; + private readonly IRunningShellTable _runningShellTable; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + + public OpenIdValidationConfiguration( + ILogger logger, + IOpenIdValidationService validationService, + IRunningShellTable runningShellTable, + IShellHost shellHost, + ShellSettings shellSettings) + { + _logger = logger; + _validationService = validationService; + _runningShellTable = runningShellTable; + _shellHost = shellHost; + _shellSettings = shellSettings; + } + + public void Configure(AuthenticationOptions options) { - private readonly ILogger _logger; - private readonly IOpenIdValidationService _validationService; - private readonly IRunningShellTable _runningShellTable; - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - - public OpenIdValidationConfiguration( - ILogger logger, - IOpenIdValidationService validationService, - IRunningShellTable runningShellTable, - IShellHost shellHost, - ShellSettings shellSettings) + var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - _logger = logger; - _validationService = validationService; - _runningShellTable = runningShellTable; - _shellHost = shellHost; - _shellSettings = shellSettings; + return; } - public void Configure(AuthenticationOptions options) + if (settings.Authority != null) { - var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) + options.AddScheme( + OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, displayName: null); + + return; + } + + // Note: the shell host guarantees that the OpenID server service resolved inside + // this using block won't be disposed until the service scope itself is released. + CreateTenantScope(settings.Tenant).UsingAsync(async scope => + { + var service = scope.ServiceProvider.GetService(); + if (service == null) { return; } - if (settings.Authority != null) + var configuration = await GetServerSettingsAsync(service); + if (configuration == null) { - options.AddScheme( - OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, displayName: null); - return; } - // Note: the shell host guarantees that the OpenID server service resolved inside - // this using block won't be disposed until the service scope itself is released. - CreateTenantScope(settings.Tenant).UsingAsync(async scope => - { - var service = scope.ServiceProvider.GetService(); - if (service == null) - { - return; - } - - var configuration = await GetServerSettingsAsync(service); - if (configuration == null) - { - return; - } + options.AddScheme( + OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, displayName: null); + }).GetAwaiter().GetResult(); + } - options.AddScheme( - OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme, displayName: null); - }).GetAwaiter().GetResult(); + public void Configure(OpenIddictValidationOptions options) + { + var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) + { + return; } - public void Configure(OpenIddictValidationOptions options) + // If the tokens are issued by an authorization server located in an Orchard tenant, retrieve the + // authority and the signing key and register them in the token validation parameters to prevent + // the handler from using an HTTP call to retrieve the discovery document from the other tenant. + // Otherwise, set the authority to allow the handler to retrieve the endpoint URLs/signing keys + // from the remote server's metadata by sending an OpenID Connect/OAuth 2.0 discovery request. + + if (settings.Authority != null) { - var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) + options.Issuer = settings.Authority; + options.ConfigurationEndpoint = settings.MetadataAddress; + options.Audiences.Add(settings.Audience); + + // Note: OpenIddict 3.0 only accepts tokens issued with a non-empty token type (e.g "at+jwt") + // or with the generic "JWT" type and a special "token_type" claim containing the actual type + // for backward compatibility, which matches the recommended best practices and helps prevent + // token substitution attacks by ensuring JWT tokens of any other type are always rejected. + // Unfortunately, most of the OAuth 2.0/OpenID Connect servers haven't been updated to emit + // access tokens using the "at+jwt" token type header. To ensure the validation handler can still + // be used with these servers, an option is provided to disable the token validation logic. + // In this case, the received tokens are assumed to be access tokens (which is the only type + // currently used in the API validation feature), no matter what their actual "typ" header is. + if (settings.DisableTokenTypeValidation) { - return; + options.TokenValidationParameters.TypeValidator = (type, token, parameters) + => JsonWebTokenTypes.AccessToken; } + } - // If the tokens are issued by an authorization server located in an Orchard tenant, retrieve the - // authority and the signing key and register them in the token validation parameters to prevent - // the handler from using an HTTP call to retrieve the discovery document from the other tenant. - // Otherwise, set the authority to allow the handler to retrieve the endpoint URLs/signing keys - // from the remote server's metadata by sending an OpenID Connect/OAuth 2.0 discovery request. - - if (settings.Authority != null) + // Note: the shell host guarantees that the OpenID server service resolved inside + // this using block won't be disposed until the service scope itself is released. + CreateTenantScope(settings.Tenant).UsingAsync(async scope => + { + var service = scope.ServiceProvider.GetService(); + if (service == null) { - options.Issuer = settings.Authority; - options.ConfigurationEndpoint = settings.MetadataAddress; - options.Audiences.Add(settings.Audience); - - // Note: OpenIddict 3.0 only accepts tokens issued with a non-empty token type (e.g "at+jwt") - // or with the generic "JWT" type and a special "token_type" claim containing the actual type - // for backward compatibility, which matches the recommended best practices and helps prevent - // token substitution attacks by ensuring JWT tokens of any other type are always rejected. - // Unfortunately, most of the OAuth 2.0/OpenID Connect servers haven't been updated to emit - // access tokens using the "at+jwt" token type header. To ensure the validation handler can still - // be used with these servers, an option is provided to disable the token validation logic. - // In this case, the received tokens are assumed to be access tokens (which is the only type - // currently used in the API validation feature), no matter what their actual "typ" header is. - if (settings.DisableTokenTypeValidation) - { - options.TokenValidationParameters.TypeValidator = (type, token, parameters) - => JsonWebTokenTypes.AccessToken; - } + return; } - // Note: the shell host guarantees that the OpenID server service resolved inside - // this using block won't be disposed until the service scope itself is released. - CreateTenantScope(settings.Tenant).UsingAsync(async scope => + var configuration = await GetServerSettingsAsync(service); + if (configuration == null) { - var service = scope.ServiceProvider.GetService(); - if (service == null) - { - return; - } - - var configuration = await GetServerSettingsAsync(service); - if (configuration == null) - { - return; - } + return; + } - options.Configuration = new OpenIddictConfiguration(); + options.Configuration = new OpenIddictConfiguration(); - options.Issuer = configuration.Authority; + options.Issuer = configuration.Authority; - // Import the signing keys from the OpenID server configuration. - foreach (var key in await service.GetSigningKeysAsync()) - { - options.Configuration.SigningKeys.Add(key); - } + // Import the signing keys from the OpenID server configuration. + foreach (var key in await service.GetSigningKeysAsync()) + { + options.Configuration.SigningKeys.Add(key); + } - // Register the encryption keys used by the OpenID Connect server. - foreach (var key in await service.GetEncryptionKeysAsync()) - { - options.EncryptionCredentials.Add(new EncryptingCredentials(key, - SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)); - } + // Register the encryption keys used by the OpenID Connect server. + foreach (var key in await service.GetEncryptionKeysAsync()) + { + options.EncryptionCredentials.Add(new EncryptingCredentials(key, + SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)); + } - // When the server is another tenant, don't allow the current tenant - // to choose the valid audiences, as this would otherwise allow it - // to validate/introspect tokens meant to be used with another tenant. - options.Audiences.Add(OpenIdConstants.Prefixes.Tenant + _shellSettings.Name); + // When the server is another tenant, don't allow the current tenant + // to choose the valid audiences, as this would otherwise allow it + // to validate/introspect tokens meant to be used with another tenant. + options.Audiences.Add(OpenIdConstants.Prefixes.Tenant + _shellSettings.Name); - // Note: token entry validation must be enabled to be able to validate reference tokens. - options.EnableTokenEntryValidation = configuration.UseReferenceAccessTokens; + // Note: token entry validation must be enabled to be able to validate reference tokens. + options.EnableTokenEntryValidation = configuration.UseReferenceAccessTokens; - // If an authority was explicitly set in the OpenID server options, - // prefer it to the dynamic tenant comparison as it's more efficient. - if (configuration.Authority != null) - { - options.TokenValidationParameters.ValidIssuer = configuration.Authority.AbsoluteUri; - } - else - { - options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) => - { - if (!Uri.TryCreate(issuer, UriKind.Absolute, out var uri)) - { - throw new SecurityTokenInvalidIssuerException("The token issuer is not valid."); - } - - var tenant = _runningShellTable.Match(HostString.FromUriComponent(uri), uri.AbsolutePath); - if (tenant == null || !string.Equals(tenant.Name, settings.Tenant, StringComparison.Ordinal)) - { - throw new SecurityTokenInvalidIssuerException("The token issuer is not valid."); - } - - return issuer; - }; - } - }).GetAwaiter().GetResult(); - } - - public void Configure(OpenIddictValidationDataProtectionOptions options) - { - var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); - if (settings == null) + // If an authority was explicitly set in the OpenID server options, + // prefer it to the dynamic tenant comparison as it's more efficient. + if (configuration.Authority != null) { - return; + options.TokenValidationParameters.ValidIssuer = configuration.Authority.AbsoluteUri; } - - // If the tokens are issued by an authorization server located in a separate tenant, - // resolve the isolated data protection provider associated with the specified tenant. - if (!string.IsNullOrEmpty(settings.Tenant) && - !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) + else { - CreateTenantScope(settings.Tenant).UsingAsync(async scope => + options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) => { - // If the other tenant is released, ensure the current tenant is also restarted as it - // relies on a data protection provider whose lifetime is managed by the other tenant. - // To make sure the other tenant is not disposed before all the pending requests are - // processed by the current tenant, a tenant dependency is manually added. - await scope.ShellContext.AddDependentShellAsync(await _shellHost.GetOrCreateShellContextAsync(_shellSettings)); - - // Note: the data protection provider is always registered as a singleton and thus will - // survive the current scope, which is mainly used to prevent the other tenant from being - // released before we have a chance to declare the current tenant as a dependent tenant. - options.DataProtectionProvider = scope.ServiceProvider.GetDataProtectionProvider(); - }).GetAwaiter().GetResult(); + if (!Uri.TryCreate(issuer, UriKind.Absolute, out var uri)) + { + throw new SecurityTokenInvalidIssuerException("The token issuer is not valid."); + } + + var tenant = _runningShellTable.Match(HostString.FromUriComponent(uri), uri.AbsolutePath); + if (tenant == null || !string.Equals(tenant.Name, settings.Tenant, StringComparison.Ordinal)) + { + throw new SecurityTokenInvalidIssuerException("The token issuer is not valid."); + } + + return issuer; + }; } - } + }).GetAwaiter().GetResult(); + } - public void Configure(string name, ApiAuthorizationOptions options) + public void Configure(OpenIddictValidationDataProtectionOptions options) + { + var settings = GetValidationSettingsAsync().GetAwaiter().GetResult(); + if (settings == null) { - // The default Orchard API authentication handler uses "Bearer" as the forwarded - // authentication scheme, that corresponds to the default value used by the JWT - // bearer handler from Microsoft. Yet, the OpenIddict validation handler uses - // a different authentication scheme, so the API scheme must be manually replaced. - options.ApiAuthenticationScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme; + return; } - public void Configure(ApiAuthorizationOptions options) - => Debug.Fail("This infrastructure method shouldn't be called."); - - private ShellScope CreateTenantScope(string tenant) + // If the tokens are issued by an authorization server located in a separate tenant, + // resolve the isolated data protection provider associated with the specified tenant. + if (!string.IsNullOrEmpty(settings.Tenant) && + !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) { - // Optimization: if the specified name corresponds to the current tenant, use the current 'ShellScope'. - if (string.IsNullOrEmpty(tenant) || string.Equals(tenant, _shellSettings.Name, StringComparison.Ordinal)) + CreateTenantScope(settings.Tenant).UsingAsync(async scope => { - return ShellScope.Current; - } - - return _shellHost.GetScopeAsync(tenant).GetAwaiter().GetResult(); + // If the other tenant is released, ensure the current tenant is also restarted as it + // relies on a data protection provider whose lifetime is managed by the other tenant. + // To make sure the other tenant is not disposed before all the pending requests are + // processed by the current tenant, a tenant dependency is manually added. + await scope.ShellContext.AddDependentShellAsync(await _shellHost.GetOrCreateShellContextAsync(_shellSettings)); + + // Note: the data protection provider is always registered as a singleton and thus will + // survive the current scope, which is mainly used to prevent the other tenant from being + // released before we have a chance to declare the current tenant as a dependent tenant. + options.DataProtectionProvider = scope.ServiceProvider.GetDataProtectionProvider(); + }).GetAwaiter().GetResult(); } + } + + public void Configure(string name, ApiAuthorizationOptions options) + { + // The default Orchard API authentication handler uses "Bearer" as the forwarded + // authentication scheme, that corresponds to the default value used by the JWT + // bearer handler from Microsoft. Yet, the OpenIddict validation handler uses + // a different authentication scheme, so the API scheme must be manually replaced. + options.ApiAuthenticationScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme; + } - private async Task GetServerSettingsAsync(IOpenIdServerService service) + public void Configure(ApiAuthorizationOptions options) + => Debug.Fail("This infrastructure method shouldn't be called."); + + private ShellScope CreateTenantScope(string tenant) + { + // Optimization: if the specified name corresponds to the current tenant, use the current 'ShellScope'. + if (string.IsNullOrEmpty(tenant) || string.Equals(tenant, _shellSettings.Name, StringComparison.Ordinal)) { - var settings = await service.GetSettingsAsync(); + return ShellScope.Current; + } + + return _shellHost.GetScopeAsync(tenant).GetAwaiter().GetResult(); + } + + private async Task GetServerSettingsAsync(IOpenIdServerService service) + { + var settings = await service.GetSettingsAsync(); - var result = await service.ValidateSettingsAsync(settings); + var result = await service.ValidateSettingsAsync(settings); - if (result.Any(result => result != ValidationResult.Success)) + if (result.Any(result => result != ValidationResult.Success)) + { + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + if (_logger.IsEnabled(LogLevel.Warning)) { - if (_logger.IsEnabled(LogLevel.Warning)) - { - var errors = result.Where(x => x != ValidationResult.Success) - .Select(x => x.ErrorMessage); + var errors = result.Where(x => x != ValidationResult.Success) + .Select(x => x.ErrorMessage); - _logger.LogWarning("The OpenID server settings are invalid: {Errors}", string.Join(SystemEnvironment.NewLine, errors)); - } - - return null; + _logger.LogWarning("The OpenID server settings are invalid: {Errors}", string.Join(SystemEnvironment.NewLine, errors)); } - } - return settings; + return null; + } } - private async Task GetValidationSettingsAsync() - { - var settings = await _validationService.GetSettingsAsync(); + return settings; + } - var result = await _validationService.ValidateSettingsAsync(settings); + private async Task GetValidationSettingsAsync() + { + var settings = await _validationService.GetSettingsAsync(); + + var result = await _validationService.ValidateSettingsAsync(settings); - if (result.Any(x => x != ValidationResult.Success)) + if (result.Any(x => x != ValidationResult.Success)) + { + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + if (_logger.IsEnabled(LogLevel.Warning)) { - if (_logger.IsEnabled(LogLevel.Warning)) - { - var errors = result.Where(x => x != ValidationResult.Success) - .Select(x => x.ErrorMessage); - - _logger.LogWarning("The OpenID validation settings are invalid: {Errors}", string.Join(SystemEnvironment.NewLine, errors)); - } + var errors = result.Where(x => x != ValidationResult.Success) + .Select(x => x.ErrorMessage); - return null; + _logger.LogWarning("The OpenID validation settings are invalid: {Errors}", string.Join(SystemEnvironment.NewLine, errors)); } - } - return settings; + return null; + } } + + return settings; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/AccessController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/AccessController.cs index d23b6b199f0..3e69861290c 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/AccessController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/AccessController.cs @@ -22,652 +22,651 @@ using OrchardCore.Users.Services; using static OpenIddict.Abstractions.OpenIddictConstants; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +// Note: the error descriptions used in this controller are deliberately not localized as +// the OAuth 2.0 specification only allows select US-ASCII characters in error_description. +[Authorize, Feature(OpenIdConstants.Features.Server)] +public class AccessController : Controller { - // Note: the error descriptions used in this controller are deliberately not localized as - // the OAuth 2.0 specification only allows select US-ASCII characters in error_description. - [Authorize, Feature(OpenIdConstants.Features.Server)] - public class AccessController : Controller + private readonly IOpenIdApplicationManager _applicationManager; + private readonly IOpenIdAuthorizationManager _authorizationManager; + private readonly IOpenIdScopeManager _scopeManager; + private readonly ShellSettings _shellSettings; + + public AccessController( + IOpenIdApplicationManager applicationManager, + IOpenIdAuthorizationManager authorizationManager, + IOpenIdScopeManager scopeManager, + ShellSettings shellSettings) { - private readonly IOpenIdApplicationManager _applicationManager; - private readonly IOpenIdAuthorizationManager _authorizationManager; - private readonly IOpenIdScopeManager _scopeManager; - private readonly ShellSettings _shellSettings; + _applicationManager = applicationManager; + _authorizationManager = authorizationManager; + _scopeManager = scopeManager; + _shellSettings = shellSettings; + } - public AccessController( - IOpenIdApplicationManager applicationManager, - IOpenIdAuthorizationManager authorizationManager, - IOpenIdScopeManager scopeManager, - ShellSettings shellSettings) + [AllowAnonymous, DisableCors, HttpGet, HttpPost, IgnoreAntiforgeryToken] + public async Task Authorize() + { + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) { - _applicationManager = applicationManager; - _authorizationManager = authorizationManager; - _scopeManager = scopeManager; - _shellSettings = shellSettings; + return View("Error", new ErrorViewModel + { + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); } - [AllowAnonymous, DisableCors, HttpGet, HttpPost, IgnoreAntiforgeryToken] - public async Task Authorize() + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) { - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) - { - return View("Error", new ErrorViewModel - { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); - } + return NotFound(); + } - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) - { - return NotFound(); - } + // Retrieve the claims stored in the authentication cookie. + // If they can't be extracted, redirect the user to the login page. + var result = await HttpContext.AuthenticateAsync(); + if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login)) + { + return RedirectToLoginPage(request); + } - // Retrieve the claims stored in the authentication cookie. - // If they can't be extracted, redirect the user to the login page. - var result = await HttpContext.AuthenticateAsync(); - if (result == null || !result.Succeeded || request.HasPrompt(Prompts.Login)) - { - return RedirectToLoginPage(request); - } + // If a max_age parameter was provided, ensure that the cookie is not too old. + // If it's too old, automatically redirect the user agent to the login page. + if (request.MaxAge != null && result.Properties.IssuedUtc != null && + DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)) + { + return RedirectToLoginPage(request); + } - // If a max_age parameter was provided, ensure that the cookie is not too old. - // If it's too old, automatically redirect the user agent to the login page. - if (request.MaxAge != null && result.Properties.IssuedUtc != null && - DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)) - { - return RedirectToLoginPage(request); - } + var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? + throw new InvalidOperationException("The application details cannot be found."); - var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? - throw new InvalidOperationException("The application details cannot be found."); + var authorizations = await _authorizationManager.FindAsync( + subject: result.Principal.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + status: Statuses.Valid, + type: AuthorizationTypes.Permanent, + scopes: request.GetScopes()).ToListAsync(); - var authorizations = await _authorizationManager.FindAsync( - subject: result.Principal.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - status: Statuses.Valid, - type: AuthorizationTypes.Permanent, - scopes: request.GetScopes()).ToListAsync(); + switch (await _applicationManager.GetConsentTypeAsync(application)) + { + case ConsentTypes.External when authorizations.Count == 0: + return Forbid(new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The logged in user is not allowed to access this client application." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - switch (await _applicationManager.GetConsentTypeAsync(application)) - { - case ConsentTypes.External when authorizations.Count == 0: - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The logged in user is not allowed to access this client application." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - - case ConsentTypes.Implicit: - case ConsentTypes.External when authorizations.Count > 0: - case ConsentTypes.Explicit when authorizations.Count > 0 && !request.HasPrompt(Prompts.Consent): - var identity = new ClaimsIdentity(result.Principal.Claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - - // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), - // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory - // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. - if (string.IsNullOrEmpty(result.Principal.FindFirst(Claims.Subject)?.Value)) - { - identity.AddClaim(new Claim(Claims.Subject, result.Principal.GetUserIdentifier())); - } - if (string.IsNullOrEmpty(result.Principal.FindFirst(Claims.Name)?.Value)) - { - identity.AddClaim(new Claim(Claims.Name, result.Principal.GetUserName())); - } - - identity.SetScopes(request.GetScopes()); - identity.SetResources(await GetResourcesAsync(request.GetScopes())); - - // Automatically create a permanent authorization to avoid requiring explicit consent - // for future authorization or token requests containing the same scopes. - var authorization = authorizations.LastOrDefault(); - authorization ??= await _authorizationManager.CreateAsync( - identity: identity, - subject: identity.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - type: AuthorizationTypes.Permanent, - scopes: identity.GetScopes()); - - identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); - identity.SetDestinations(GetDestinations); - - return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - - case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "Interactive user consent is required." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - - default: - return View(new AuthorizeViewModel - { - ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), - RequestId = request.RequestId, - Scope = request.Scope - }); - } + case ConsentTypes.Implicit: + case ConsentTypes.External when authorizations.Count > 0: + case ConsentTypes.Explicit when authorizations.Count > 0 && !request.HasPrompt(Prompts.Consent): + var identity = new ClaimsIdentity(result.Principal.Claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - IActionResult RedirectToLoginPage(OpenIddictRequest request) - { - // If the client application requested promptless authentication, - // return an error indicating that the user is not logged in. - if (request.HasPrompt(Prompts.None)) + // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), + // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory + // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. + if (string.IsNullOrEmpty(result.Principal.FindFirst(Claims.Subject)?.Value)) { - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + identity.AddClaim(new Claim(Claims.Subject, result.Principal.GetUserIdentifier())); } - - string GetRedirectUrl() + if (string.IsNullOrEmpty(result.Principal.FindFirst(Claims.Name)?.Value)) { - // Override the prompt parameter to prevent infinite authentication/authorization loops. - var parameters = Request.Query.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - parameters[Parameters.Prompt] = "continue"; - - return Request.PathBase + Request.Path + QueryString.Create(parameters); + identity.AddClaim(new Claim(Claims.Name, result.Principal.GetUserName())); } - return Challenge(new AuthenticationProperties + identity.SetScopes(request.GetScopes()); + identity.SetResources(await GetResourcesAsync(request.GetScopes())); + + // Automatically create a permanent authorization to avoid requiring explicit consent + // for future authorization or token requests containing the same scopes. + var authorization = authorizations.LastOrDefault(); + authorization ??= await _authorizationManager.CreateAsync( + identity: identity, + subject: identity.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + type: AuthorizationTypes.Permanent, + scopes: identity.GetScopes()); + + identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); + identity.SetDestinations(GetDestinations); + + return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + + case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): + return Forbid(new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "Interactive user consent is required." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + + default: + return View(new AuthorizeViewModel { - RedirectUri = GetRedirectUrl() + ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), + RequestId = request.RequestId, + Scope = request.Scope }); - } } - [ActionName(nameof(Authorize)), DisableCors] - [FormValueRequired("submit.Accept"), HttpPost] - public async Task AuthorizeAccept() + IActionResult RedirectToLoginPage(OpenIddictRequest request) { - // Warning: unlike the main Authorize method, this method MUST NOT be decorated with - // [IgnoreAntiforgeryToken] as we must be able to reject authorization requests - // sent by a malicious client that could abuse this interactive endpoint to silently - // get codes/tokens without the user explicitly approving the authorization demand. - - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) + // If the client application requested promptless authentication, + // return an error indicating that the user is not logged in. + if (request.HasPrompt(Prompts.None)) { - return View("Error", new ErrorViewModel + return Forbid(new AuthenticationProperties(new Dictionary { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) + string GetRedirectUrl() { - return NotFound(); + // Override the prompt parameter to prevent infinite authentication/authorization loops. + var parameters = Request.Query.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + parameters[Parameters.Prompt] = "continue"; + + return Request.PathBase + Request.Path + QueryString.Create(parameters); } - var application = await _applicationManager.FindByClientIdAsync(request.ClientId) - ?? throw new InvalidOperationException("The application details cannot be found."); + return Challenge(new AuthenticationProperties + { + RedirectUri = GetRedirectUrl() + }); + } + } - var authorizations = await _authorizationManager.FindAsync( - subject: User.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - status: Statuses.Valid, - type: AuthorizationTypes.Permanent, - scopes: request.GetScopes()).ToListAsync(); + [ActionName(nameof(Authorize)), DisableCors] + [FormValueRequired("submit.Accept"), HttpPost] + public async Task AuthorizeAccept() + { + // Warning: unlike the main Authorize method, this method MUST NOT be decorated with + // [IgnoreAntiforgeryToken] as we must be able to reject authorization requests + // sent by a malicious client that could abuse this interactive endpoint to silently + // get codes/tokens without the user explicitly approving the authorization demand. - // Note: the same check is already made in the GET action but is repeated - // here to ensure a malicious user can't abuse this POST endpoint and - // force it to return a valid response without the external authorization. - switch (await _applicationManager.GetConsentTypeAsync(application)) + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) + { + return View("Error", new ErrorViewModel { - case ConsentTypes.External when authorizations.Count == 0: - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The logged in user is not allowed to access this client application." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - - default: - var identity = new ClaimsIdentity(User.Claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - - // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), - // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory - // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. - if (string.IsNullOrEmpty(User.FindFirst(Claims.Subject)?.Value)) - { - identity.AddClaim(new Claim(Claims.Subject, User.GetUserIdentifier())); - } - if (string.IsNullOrEmpty(User.FindFirst(Claims.Name)?.Value)) - { - identity.AddClaim(new Claim(Claims.Name, User.GetUserName())); - } - - identity.SetScopes(request.GetScopes()); - identity.SetResources(await GetResourcesAsync(request.GetScopes())); - - // Automatically create a permanent authorization to avoid requiring explicit consent - // for future authorization or token requests containing the same scopes. - var authorization = authorizations.LastOrDefault(); - authorization ??= await _authorizationManager.CreateAsync( - identity: identity, - subject: identity.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - type: AuthorizationTypes.Permanent, - scopes: identity.GetScopes()); - - identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); - identity.SetDestinations(GetDestinations); - - return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); } - [ActionName(nameof(Authorize)), DisableCors] - [FormValueRequired("submit.Deny"), HttpPost] - public IActionResult AuthorizeDeny() + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) { - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) - { - return View("Error", new ErrorViewModel - { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); - } + return NotFound(); + } - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) - { - return NotFound(); - } + var application = await _applicationManager.FindByClientIdAsync(request.ClientId) + ?? throw new InvalidOperationException("The application details cannot be found."); - return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + var authorizations = await _authorizationManager.FindAsync( + subject: User.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + status: Statuses.Valid, + type: AuthorizationTypes.Permanent, + scopes: request.GetScopes()).ToListAsync(); - [AllowAnonymous, DisableCors, HttpGet, HttpPost, IgnoreAntiforgeryToken] - public async Task Logout() + // Note: the same check is already made in the GET action but is repeated + // here to ensure a malicious user can't abuse this POST endpoint and + // force it to return a valid response without the external authorization. + switch (await _applicationManager.GetConsentTypeAsync(application)) { - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) - { - return View("Error", new ErrorViewModel + case ConsentTypes.External when authorizations.Count == 0: + return Forbid(new AuthenticationProperties(new Dictionary { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); - } + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The logged in user is not allowed to access this client application." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) - { - return NotFound(); - } + default: + var identity = new ClaimsIdentity(User.Claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri)) - { - // If the user is not logged in, allow redirecting the user agent back to the - // specified post_logout_redirect_uri without rendering a confirmation form. - var result = await HttpContext.AuthenticateAsync(); - if (result == null || !result.Succeeded) + // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), + // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory + // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. + if (string.IsNullOrEmpty(User.FindFirst(Claims.Subject)?.Value)) { - return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + identity.AddClaim(new Claim(Claims.Subject, User.GetUserIdentifier())); + } + if (string.IsNullOrEmpty(User.FindFirst(Claims.Name)?.Value)) + { + identity.AddClaim(new Claim(Claims.Name, User.GetUserName())); } - } - return View(new LogoutViewModel - { - RequestId = request.RequestId - }); + identity.SetScopes(request.GetScopes()); + identity.SetResources(await GetResourcesAsync(request.GetScopes())); + + // Automatically create a permanent authorization to avoid requiring explicit consent + // for future authorization or token requests containing the same scopes. + var authorization = authorizations.LastOrDefault(); + authorization ??= await _authorizationManager.CreateAsync( + identity: identity, + subject: identity.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + type: AuthorizationTypes.Permanent, + scopes: identity.GetScopes()); + + identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); + identity.SetDestinations(GetDestinations); + + return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } + } - [ActionName(nameof(Logout)), AllowAnonymous, DisableCors] - [FormValueRequired("submit.Accept"), HttpPost] - public async Task LogoutAccept() + [ActionName(nameof(Authorize)), DisableCors] + [FormValueRequired("submit.Deny"), HttpPost] + public IActionResult AuthorizeDeny() + { + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) { - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) - { - return View("Error", new ErrorViewModel - { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); - } - - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) + return View("Error", new ErrorViewModel { - return NotFound(); - } + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } - // Warning: unlike the main Logout method, this method MUST NOT be decorated with - // [IgnoreAntiforgeryToken] as we must be able to reject end session requests - // sent by a malicious client that could abuse this interactive endpoint to silently - // log the user out without the user explicitly approving the log out operation. + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return NotFound(); + } - await HttpContext.SignOutAsync(); + return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - // If no post_logout_redirect_uri was specified, redirect the user agent - // to the root page, that should correspond to the home page in most cases. - if (string.IsNullOrEmpty(request.PostLogoutRedirectUri)) + [AllowAnonymous, DisableCors, HttpGet, HttpPost, IgnoreAntiforgeryToken] + public async Task Logout() + { + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) + { + return View("Error", new ErrorViewModel { - return Redirect("~/"); - } + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } - return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return NotFound(); } - [ActionName(nameof(Logout)), AllowAnonymous, DisableCors] - [FormValueRequired("submit.Deny"), HttpPost] - public IActionResult LogoutDeny() + if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri)) { - var response = HttpContext.GetOpenIddictServerResponse(); - if (response != null) + // If the user is not logged in, allow redirecting the user agent back to the + // specified post_logout_redirect_uri without rendering a confirmation form. + var result = await HttpContext.AuthenticateAsync(); + if (result == null || !result.Succeeded) { - return View("Error", new ErrorViewModel - { - Error = response.Error, - ErrorDescription = response.ErrorDescription - }); + return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } + } - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) + return View(new LogoutViewModel + { + RequestId = request.RequestId + }); + } + + [ActionName(nameof(Logout)), AllowAnonymous, DisableCors] + [FormValueRequired("submit.Accept"), HttpPost] + public async Task LogoutAccept() + { + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) + { + return View("Error", new ErrorViewModel { - return NotFound(); - } + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } - return Redirect("~/"); + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return NotFound(); } - [AllowAnonymous, HttpPost] - [IgnoreAntiforgeryToken] - [Produces("application/json")] - public Task Token() + // Warning: unlike the main Logout method, this method MUST NOT be decorated with + // [IgnoreAntiforgeryToken] as we must be able to reject end session requests + // sent by a malicious client that could abuse this interactive endpoint to silently + // log the user out without the user explicitly approving the log out operation. + + await HttpContext.SignOutAsync(); + + // If no post_logout_redirect_uri was specified, redirect the user agent + // to the root page, that should correspond to the home page in most cases. + if (string.IsNullOrEmpty(request.PostLogoutRedirectUri)) { - // Warning: this action is decorated with IgnoreAntiforgeryTokenAttribute to override - // the global antiforgery token validation policy applied by the MVC modules stack, - // which is required for this stateless OAuth2/OIDC token endpoint to work correctly. - // To prevent effective CSRF/session fixation attacks, this action MUST NOT return - // an authentication cookie or try to establish an ASP.NET Core user session. + return Redirect("~/"); + } - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) - { - return Task.FromResult((IActionResult)NotFound()); - } + return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - if (request.IsPasswordGrantType()) + [ActionName(nameof(Logout)), AllowAnonymous, DisableCors] + [FormValueRequired("submit.Deny"), HttpPost] + public IActionResult LogoutDeny() + { + var response = HttpContext.GetOpenIddictServerResponse(); + if (response != null) + { + return View("Error", new ErrorViewModel { - return ExchangePasswordGrantType(request); - } + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } - if (request.IsClientCredentialsGrantType()) - { - return ExchangeClientCredentialsGrantType(request); - } + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return NotFound(); + } - if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) - { - return ExchangeAuthorizationCodeOrRefreshTokenGrantType(request); - } + return Redirect("~/"); + } + + [AllowAnonymous, HttpPost] + [IgnoreAntiforgeryToken] + [Produces("application/json")] + public Task Token() + { + // Warning: this action is decorated with IgnoreAntiforgeryTokenAttribute to override + // the global antiforgery token validation policy applied by the MVC modules stack, + // which is required for this stateless OAuth2/OIDC token endpoint to work correctly. + // To prevent effective CSRF/session fixation attacks, this action MUST NOT return + // an authentication cookie or try to establish an ASP.NET Core user session. + + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return Task.FromResult((IActionResult)NotFound()); + } + + if (request.IsPasswordGrantType()) + { + return ExchangePasswordGrantType(request); + } + + if (request.IsClientCredentialsGrantType()) + { + return ExchangeClientCredentialsGrantType(request); + } - throw new NotSupportedException("The specified grant type is not supported."); + if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) + { + return ExchangeAuthorizationCodeOrRefreshTokenGrantType(request); } - private async Task ExchangeClientCredentialsGrantType(OpenIddictRequest request) + throw new NotSupportedException("The specified grant type is not supported."); + } + + private async Task ExchangeClientCredentialsGrantType(OpenIddictRequest request) + { + if (request.HasScope(Scopes.OfflineAccess)) { - if (request.HasScope(Scopes.OfflineAccess)) + return Forbid(new AuthenticationProperties(new Dictionary { - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidScope, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The 'offline_access' scope is not allowed when using the client credentials grant." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidScope, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The 'offline_access' scope is not allowed when using the client credentials grant." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - // Note: client authentication is always enforced by OpenIddict before this action is invoked. - var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? - throw new InvalidOperationException("The application details cannot be found."); + // Note: client authentication is always enforced by OpenIddict before this action is invoked. + var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? + throw new InvalidOperationException("The application details cannot be found."); - var identity = new ClaimsIdentity( - OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, - Claims.Name, Claims.Role); + var identity = new ClaimsIdentity( + OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + Claims.Name, Claims.Role); - identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.Application)); - identity.AddClaim(new Claim(Claims.Subject, request.ClientId)); + identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.Application)); + identity.AddClaim(new Claim(Claims.Subject, request.ClientId)); - // Always add a "name" claim for grant_type=client_credentials in both - // access and identity tokens even if the "name" scope wasn't requested. - identity.AddClaim(new Claim(Claims.Name, await _applicationManager.GetDisplayNameAsync(application)) - .SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); + // Always add a "name" claim for grant_type=client_credentials in both + // access and identity tokens even if the "name" scope wasn't requested. + identity.AddClaim(new Claim(Claims.Name, await _applicationManager.GetDisplayNameAsync(application)) + .SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); - // If the role service is available, add all the role claims - // associated with the application roles in the database. - var roleService = HttpContext.RequestServices.GetService(); + // If the role service is available, add all the role claims + // associated with the application roles in the database. + var roleService = HttpContext.RequestServices.GetService(); - foreach (var role in await _applicationManager.GetRolesAsync(application)) - { - // Since the claims added in this block have a dynamic name, directly set the destinations - // here instead of relying on the GetDestination() helper that only works with static claims. + foreach (var role in await _applicationManager.GetRolesAsync(application)) + { + // Since the claims added in this block have a dynamic name, directly set the destinations + // here instead of relying on the GetDestination() helper that only works with static claims. - identity.AddClaim(new Claim(identity.RoleClaimType, role) - .SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); + identity.AddClaim(new Claim(identity.RoleClaimType, role) + .SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); - if (roleService != null) + if (roleService != null) + { + foreach (var claim in await roleService.GetRoleClaimsAsync(role)) { - foreach (var claim in await roleService.GetRoleClaimsAsync(role)) - { - identity.AddClaim(claim.SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); - } + identity.AddClaim(claim.SetDestinations(Destinations.AccessToken, Destinations.IdentityToken)); } } + } - identity.SetScopes(request.GetScopes()); - identity.SetResources(await GetResourcesAsync(request.GetScopes())); - identity.SetDestinations(GetDestinations); + identity.SetScopes(request.GetScopes()); + identity.SetResources(await GetResourcesAsync(request.GetScopes())); + identity.SetDestinations(GetDestinations); - return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - private async Task ExchangePasswordGrantType(OpenIddictRequest request) - { - var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? - throw new InvalidOperationException("The application details cannot be found."); + private async Task ExchangePasswordGrantType(OpenIddictRequest request) + { + var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? + throw new InvalidOperationException("The application details cannot be found."); - // By design, the password flow requires direct username/password validation, which is performed by - // the user service. If this service is not registered, prevent the password flow from being used. - var service = HttpContext.RequestServices.GetService(); - if (service == null) + // By design, the password flow requires direct username/password validation, which is performed by + // the user service. If this service is not registered, prevent the password flow from being used. + var service = HttpContext.RequestServices.GetService(); + if (service == null) + { + return Forbid(new AuthenticationProperties(new Dictionary { - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnsupportedGrantType, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The resource owner password credentials grant is not supported." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnsupportedGrantType, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The resource owner password credentials grant is not supported." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - string error = null; - var user = await service.AuthenticateAsync(request.Username, request.Password, (key, message) => error = message); - if (user == null) + string error = null; + var user = await service.AuthenticateAsync(request.Username, request.Password, (key, message) => error = message); + if (user == null) + { + return Forbid(new AuthenticationProperties(new Dictionary { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = error + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + var principal = await service.CreatePrincipalAsync(user); + + var authorizations = await _authorizationManager.FindAsync( + subject: principal.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + status: Statuses.Valid, + type: AuthorizationTypes.Permanent, + scopes: request.GetScopes()).ToListAsync(); + + // If the application is configured to use external consent, + // reject the request if no existing authorization can be found. + switch (await _applicationManager.GetConsentTypeAsync(application)) + { + case ConsentTypes.External when authorizations.Count == 0: return Forbid(new AuthenticationProperties(new Dictionary { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = error + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The logged in user is not allowed to access this client application." }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } - - var principal = await service.CreatePrincipalAsync(user); + } - var authorizations = await _authorizationManager.FindAsync( - subject: principal.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - status: Statuses.Valid, - type: AuthorizationTypes.Permanent, - scopes: request.GetScopes()).ToListAsync(); + var identity = (ClaimsIdentity)principal.Identity; + identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - // If the application is configured to use external consent, - // reject the request if no existing authorization can be found. - switch (await _applicationManager.GetConsentTypeAsync(application)) - { - case ConsentTypes.External when authorizations.Count == 0: - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The logged in user is not allowed to access this client application." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } - - var identity = (ClaimsIdentity)principal.Identity; - identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); + // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), + // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory + // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. + if (string.IsNullOrEmpty(principal.FindFirst(Claims.Subject)?.Value)) + { + identity.AddClaim(new Claim(Claims.Subject, principal.GetUserIdentifier())); + } + if (string.IsNullOrEmpty(principal.FindFirst(Claims.Name)?.Value)) + { + identity.AddClaim(new Claim(Claims.Name, principal.GetUserName())); + } - // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), - // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory - // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. - if (string.IsNullOrEmpty(principal.FindFirst(Claims.Subject)?.Value)) - { - identity.AddClaim(new Claim(Claims.Subject, principal.GetUserIdentifier())); - } - if (string.IsNullOrEmpty(principal.FindFirst(Claims.Name)?.Value)) - { - identity.AddClaim(new Claim(Claims.Name, principal.GetUserName())); - } + identity.SetScopes(request.GetScopes()); + identity.SetResources(await GetResourcesAsync(request.GetScopes())); - identity.SetScopes(request.GetScopes()); - identity.SetResources(await GetResourcesAsync(request.GetScopes())); + // Automatically create a permanent authorization to avoid requiring explicit consent + // for future authorization or token requests containing the same scopes. + var authorization = authorizations.FirstOrDefault(); + authorization ??= await _authorizationManager.CreateAsync( + identity: identity, + subject: identity.GetUserIdentifier(), + client: await _applicationManager.GetIdAsync(application), + type: AuthorizationTypes.Permanent, + scopes: identity.GetScopes()); - // Automatically create a permanent authorization to avoid requiring explicit consent - // for future authorization or token requests containing the same scopes. - var authorization = authorizations.FirstOrDefault(); - authorization ??= await _authorizationManager.CreateAsync( - identity: identity, - subject: identity.GetUserIdentifier(), - client: await _applicationManager.GetIdAsync(application), - type: AuthorizationTypes.Permanent, - scopes: identity.GetScopes()); + identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); + identity.SetDestinations(GetDestinations); - identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); - identity.SetDestinations(GetDestinations); + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } - return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + private async Task ExchangeAuthorizationCodeOrRefreshTokenGrantType(OpenIddictRequest request) + { + // Retrieve the claims principal stored in the authorization code/refresh token. + var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme) ?? + throw new InvalidOperationException("The user principal cannot be resolved."); - private async Task ExchangeAuthorizationCodeOrRefreshTokenGrantType(OpenIddictRequest request) + if (request.IsRefreshTokenGrantType()) { - // Retrieve the claims principal stored in the authorization code/refresh token. - var info = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme) ?? - throw new InvalidOperationException("The user principal cannot be resolved."); - - if (request.IsRefreshTokenGrantType()) + var type = info.Principal.FindFirst(OpenIdConstants.Claims.EntityType)?.Value; + if (!string.Equals(type, OpenIdConstants.EntityTypes.User, StringComparison.Ordinal)) { - var type = info.Principal.FindFirst(OpenIdConstants.Claims.EntityType)?.Value; - if (!string.Equals(type, OpenIdConstants.EntityTypes.User, StringComparison.Ordinal)) + return Forbid(new AuthenticationProperties(new Dictionary { - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnauthorizedClient, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The refresh token grant type is not allowed for refresh tokens retrieved using the client credentials flow." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.UnauthorizedClient, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The refresh token grant type is not allowed for refresh tokens retrieved using the client credentials flow." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } + } - // By default, re-use the principal stored in the authorization code/refresh token. - var principal = info.Principal; + // By default, re-use the principal stored in the authorization code/refresh token. + var principal = info.Principal; - // If the user service is available, try to refresh the principal by retrieving - // the user object from the database and creating a new claims-based principal. - var service = HttpContext.RequestServices.GetService(); - if (service != null) + // If the user service is available, try to refresh the principal by retrieving + // the user object from the database and creating a new claims-based principal. + var service = HttpContext.RequestServices.GetService(); + if (service != null) + { + var user = await service.GetUserByUniqueIdAsync(principal.GetUserIdentifier()); + if (user != null) { - var user = await service.GetUserByUniqueIdAsync(principal.GetUserIdentifier()); - if (user != null) - { - principal = await service.CreatePrincipalAsync(user); - // Copy the granted scopes and resources from the original authorization code/refresh token principal - principal.SetScopes(info.Principal.GetScopes()); - principal.SetResources(await GetResourcesAsync(info.Principal.GetScopes())); - } + principal = await service.CreatePrincipalAsync(user); + // Copy the granted scopes and resources from the original authorization code/refresh token principal + principal.SetScopes(info.Principal.GetScopes()); + principal.SetResources(await GetResourcesAsync(info.Principal.GetScopes())); } + } - var identity = (ClaimsIdentity)principal.Identity; - identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); + var identity = (ClaimsIdentity)principal.Identity; + identity.AddClaim(new Claim(OpenIdConstants.Claims.EntityType, OpenIdConstants.EntityTypes.User)); - // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), - // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory - // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. - if (string.IsNullOrEmpty(principal.FindFirst(Claims.Subject)?.Value)) - { - identity.AddClaim(new Claim(Claims.Subject, principal.GetUserIdentifier())); - } - if (string.IsNullOrEmpty(principal.FindFirst(Claims.Name)?.Value)) - { - identity.AddClaim(new Claim(Claims.Name, principal.GetUserName())); - } + // Note: while ASP.NET Core Identity uses the legacy WS-Federation claims (exposed by the ClaimTypes class), + // OpenIddict uses the newer JWT claims defined by the OpenID Connect specification. To ensure the mandatory + // subject claim is correctly populated (and avoid an InvalidOperationException), it's manually added here. + if (string.IsNullOrEmpty(principal.FindFirst(Claims.Subject)?.Value)) + { + identity.AddClaim(new Claim(Claims.Subject, principal.GetUserIdentifier())); + } + if (string.IsNullOrEmpty(principal.FindFirst(Claims.Name)?.Value)) + { + identity.AddClaim(new Claim(Claims.Name, principal.GetUserName())); + } - identity.SetDestinations(GetDestinations); + identity.SetDestinations(GetDestinations); - return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); - } + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + private static IEnumerable GetDestinations(Claim claim) + { + // Note: by default, claims are NOT automatically included in the access and identity tokens. + // To allow OpenIddict to serialize them, you must attach them a destination, that specifies + // whether they should be included in access tokens, in identity tokens or in both. - private static IEnumerable GetDestinations(Claim claim) + switch (claim.Type) { - // Note: by default, claims are NOT automatically included in the access and identity tokens. - // To allow OpenIddict to serialize them, you must attach them a destination, that specifies - // whether they should be included in access tokens, in identity tokens or in both. + // If the claim already includes destinations (set before this helper is called), flow them as-is. + case string when claim.GetDestinations() is { IsDefaultOrEmpty: false } destinations: + return destinations; + + // Never include the security stamp in the access and identity tokens, as it's a secret value. + case "AspNet.Identity.SecurityStamp": + return []; + + // Only add the claim to the id_token if the corresponding scope was granted. + // The other claims will only be added to the access_token. + case OpenIdConstants.Claims.EntityType: + case Claims.Name when claim.Subject.HasScope(Scopes.Profile): + case Claims.Email when claim.Subject.HasScope(Scopes.Email): + case Claims.Role when claim.Subject.HasScope(Scopes.Roles): + return new[] + { + Destinations.AccessToken, + Destinations.IdentityToken + }; - switch (claim.Type) - { - // If the claim already includes destinations (set before this helper is called), flow them as-is. - case string when claim.GetDestinations() is { IsDefaultOrEmpty: false } destinations: - return destinations; - - // Never include the security stamp in the access and identity tokens, as it's a secret value. - case "AspNet.Identity.SecurityStamp": - return []; - - // Only add the claim to the id_token if the corresponding scope was granted. - // The other claims will only be added to the access_token. - case OpenIdConstants.Claims.EntityType: - case Claims.Name when claim.Subject.HasScope(Scopes.Profile): - case Claims.Email when claim.Subject.HasScope(Scopes.Email): - case Claims.Role when claim.Subject.HasScope(Scopes.Roles): - return new[] - { - Destinations.AccessToken, - Destinations.IdentityToken - }; - - default: return new[] { Destinations.AccessToken }; - } + default: return new[] { Destinations.AccessToken }; } + } - private async Task> GetResourcesAsync(ImmutableArray scopes) + private async Task> GetResourcesAsync(ImmutableArray scopes) + { + // Note: the current tenant name is always added as a valid resource/audience, + // which allows the end user to use the corresponding tokens with the APIs + // located in the current tenant without having to explicitly register a scope. + var resources = new List() { - // Note: the current tenant name is always added as a valid resource/audience, - // which allows the end user to use the corresponding tokens with the APIs - // located in the current tenant without having to explicitly register a scope. - var resources = new List() - { - OpenIdConstants.Prefixes.Tenant + _shellSettings.Name - }; + OpenIdConstants.Prefixes.Tenant + _shellSettings.Name + }; - await foreach (var resource in _scopeManager.ListResourcesAsync(scopes)) - { - resources.Add(resource); - } - - return resources; + await foreach (var resource in _scopeManager.ListResourcesAsync(scopes)) + { + resources.Add(resource); } + + return resources; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ApplicationController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ApplicationController.cs index 0d02e2ba2ad..62b965ce821 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ApplicationController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ApplicationController.cs @@ -22,381 +22,380 @@ using OrchardCore.OpenId.ViewModels; using OrchardCore.Security.Services; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +[Feature(OpenIdConstants.Features.Management)] +[Admin("OpenId/Application/{action}/{id?}", "OpenIdApplication{action}")] +public class ApplicationController : Controller { - [Feature(OpenIdConstants.Features.Management)] - [Admin("OpenId/Application/{action}/{id?}", "OpenIdApplication{action}")] - public class ApplicationController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IShapeFactory _shapeFactory; + private readonly PagerOptions _pagerOptions; + private readonly IOpenIdApplicationManager _applicationManager; + private readonly IOpenIdScopeManager _scopeManager; + private readonly INotifier _notifier; + private readonly ShellDescriptor _shellDescriptor; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public ApplicationController( + IShapeFactory shapeFactory, + IOptions pagerOptions, + IStringLocalizer stringLocalizer, + IAuthorizationService authorizationService, + IOpenIdApplicationManager applicationManager, + IOpenIdScopeManager scopeManager, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + ShellDescriptor shellDescriptor) { - private readonly IAuthorizationService _authorizationService; - private readonly IShapeFactory _shapeFactory; - private readonly PagerOptions _pagerOptions; - private readonly IOpenIdApplicationManager _applicationManager; - private readonly IOpenIdScopeManager _scopeManager; - private readonly INotifier _notifier; - private readonly ShellDescriptor _shellDescriptor; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public ApplicationController( - IShapeFactory shapeFactory, - IOptions pagerOptions, - IStringLocalizer stringLocalizer, - IAuthorizationService authorizationService, - IOpenIdApplicationManager applicationManager, - IOpenIdScopeManager scopeManager, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - ShellDescriptor shellDescriptor) + _shapeFactory = shapeFactory; + _pagerOptions = pagerOptions.Value; + S = stringLocalizer; + H = htmlLocalizer; + _authorizationService = authorizationService; + _applicationManager = applicationManager; + _scopeManager = scopeManager; + _notifier = notifier; + _shellDescriptor = shellDescriptor; + } + + [Admin("OpenId/Application", "OpenIdApplication")] + public async Task Index(PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) { - _shapeFactory = shapeFactory; - _pagerOptions = pagerOptions.Value; - S = stringLocalizer; - H = htmlLocalizer; - _authorizationService = authorizationService; - _applicationManager = applicationManager; - _scopeManager = scopeManager; - _notifier = notifier; - _shellDescriptor = shellDescriptor; + return Forbid(); } - [Admin("OpenId/Application", "OpenIdApplication")] - public async Task Index(PagerParameters pagerParameters) + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var count = await _applicationManager.CountAsync(); + + var applications = new List(); + + await foreach (var application in _applicationManager.ListAsync(pager.PageSize, pager.GetStartIndex())) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + applications.Add(new OpenIdApplicationEntry { - return Forbid(); - } + DisplayName = await _applicationManager.GetDisplayNameAsync(application), + Id = await _applicationManager.GetPhysicalIdAsync(application) + }); + } - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var count = await _applicationManager.CountAsync(); + var model = new OpenIdApplicationsIndexViewModel + { + Pager = await _shapeFactory.PagerAsync(pager, (int)count), + Applications = applications.OrderBy(x => x.DisplayName) + .ThenBy(x => x.Id) + .ToArray(), + }; - var applications = new List(); + return View(model); + } - await foreach (var application in _applicationManager.ListAsync(pager.PageSize, pager.GetStartIndex())) + [HttpGet] + public async Task Create(string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + { + return Forbid(); + } + + var model = new CreateOpenIdApplicationViewModel(); + + var roleService = HttpContext.RequestServices?.GetService(); + if (roleService != null) + { + foreach (var role in await roleService.GetRoleNamesAsync()) { - applications.Add(new OpenIdApplicationEntry + model.RoleEntries.Add(new CreateOpenIdApplicationViewModel.RoleEntry { - DisplayName = await _applicationManager.GetDisplayNameAsync(application), - Id = await _applicationManager.GetPhysicalIdAsync(application) + Name = role }); } - - var model = new OpenIdApplicationsIndexViewModel - { - Pager = await _shapeFactory.PagerAsync(pager, (int)count), - Applications = applications.OrderBy(x => x.DisplayName) - .ThenBy(x => x.Id) - .ToArray(), - }; - - return View(model); + } + else + { + await _notifier.WarningAsync(H["There are no registered services to provide roles."]); } - [HttpGet] - public async Task Create(string returnUrl = null) + await foreach (var scope in _scopeManager.ListAsync(null, null, default)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + model.ScopeEntries.Add(new CreateOpenIdApplicationViewModel.ScopeEntry { - return Forbid(); - } + Name = await _scopeManager.GetNameAsync(scope) + }); + } - var model = new CreateOpenIdApplicationViewModel(); + ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - var roleService = HttpContext.RequestServices?.GetService(); - if (roleService != null) - { - foreach (var role in await roleService.GetRoleNamesAsync()) - { - model.RoleEntries.Add(new CreateOpenIdApplicationViewModel.RoleEntry - { - Name = role - }); - } - } - else - { - await _notifier.WarningAsync(H["There are no registered services to provide roles."]); - } + [HttpPost] + public async Task Create(CreateOpenIdApplicationViewModel model, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + { + return Forbid(); + } - await foreach (var scope in _scopeManager.ListAsync(null, null, default)) - { - model.ScopeEntries.Add(new CreateOpenIdApplicationViewModel.ScopeEntry - { - Name = await _scopeManager.GetNameAsync(scope) - }); - } + if (!string.IsNullOrEmpty(model.ClientSecret) && + string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError(nameof(model.ClientSecret), S["No client secret can be set for public applications."]); + } + else if (string.IsNullOrEmpty(model.ClientSecret) && + string.Equals(model.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError(nameof(model.ClientSecret), S["The client secret is required for confidential applications."]); + } + + if (!string.IsNullOrEmpty(model.ClientId) && await _applicationManager.FindByClientIdAsync(model.ClientId) != null) + { + ModelState.AddModelError(nameof(model.ClientId), S["The client identifier is already taken by another application."]); + } + if (!ModelState.IsValid) + { ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); ViewData["ReturnUrl"] = returnUrl; return View(model); } - [HttpPost] - public async Task Create(CreateOpenIdApplicationViewModel model, string returnUrl = null) + var settings = new OpenIdApplicationSettings() { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) - { - return Forbid(); - } - - if (!string.IsNullOrEmpty(model.ClientSecret) && - string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError(nameof(model.ClientSecret), S["No client secret can be set for public applications."]); - } - else if (string.IsNullOrEmpty(model.ClientSecret) && - string.Equals(model.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError(nameof(model.ClientSecret), S["The client secret is required for confidential applications."]); - } - - if (!string.IsNullOrEmpty(model.ClientId) && await _applicationManager.FindByClientIdAsync(model.ClientId) != null) - { - ModelState.AddModelError(nameof(model.ClientId), S["The client identifier is already taken by another application."]); - } - - if (!ModelState.IsValid) - { - ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); - ViewData["ReturnUrl"] = returnUrl; - return View(model); - } + AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, + AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, + AllowHybridFlow = model.AllowHybridFlow, + AllowImplicitFlow = model.AllowImplicitFlow, + AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, + AllowLogoutEndpoint = model.AllowLogoutEndpoint, + AllowPasswordFlow = model.AllowPasswordFlow, + AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, + AllowRevocationEndpoint = model.AllowRevocationEndpoint, + ClientId = model.ClientId, + ClientSecret = model.ClientSecret, + ConsentType = model.ConsentType, + DisplayName = model.DisplayName, + PostLogoutRedirectUris = model.PostLogoutRedirectUris, + RedirectUris = model.RedirectUris, + Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), + Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), + Type = model.Type, + RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange + }; + + await _applicationManager.UpdateDescriptorFromSettings(settings); + + if (string.IsNullOrEmpty(returnUrl)) + { + return RedirectToAction(nameof(Index)); + } - var settings = new OpenIdApplicationSettings() - { - AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, - AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, - AllowHybridFlow = model.AllowHybridFlow, - AllowImplicitFlow = model.AllowImplicitFlow, - AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, - AllowLogoutEndpoint = model.AllowLogoutEndpoint, - AllowPasswordFlow = model.AllowPasswordFlow, - AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, - AllowRevocationEndpoint = model.AllowRevocationEndpoint, - ClientId = model.ClientId, - ClientSecret = model.ClientSecret, - ConsentType = model.ConsentType, - DisplayName = model.DisplayName, - PostLogoutRedirectUris = model.PostLogoutRedirectUris, - RedirectUris = model.RedirectUris, - Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), - Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), - Type = model.Type, - RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange - }; - - await _applicationManager.UpdateDescriptorFromSettings(settings); - - if (string.IsNullOrEmpty(returnUrl)) - { - return RedirectToAction(nameof(Index)); - } + return this.LocalRedirect(returnUrl, true); + } - return this.LocalRedirect(returnUrl, true); + public async Task Edit(string id, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + { + return Forbid(); } - public async Task Edit(string id, string returnUrl = null) + var application = await _applicationManager.FindByPhysicalIdAsync(id); + if (application == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) - { - return Forbid(); - } - - var application = await _applicationManager.FindByPhysicalIdAsync(id); - if (application == null) - { - return NotFound(); - } + return NotFound(); + } - ValueTask HasPermissionAsync(string permission) => _applicationManager.HasPermissionAsync(application, permission); - ValueTask HasRequirementAsync(string requirement) => _applicationManager.HasRequirementAsync(application, requirement); + ValueTask HasPermissionAsync(string permission) => _applicationManager.HasPermissionAsync(application, permission); + ValueTask HasRequirementAsync(string requirement) => _applicationManager.HasRequirementAsync(application, requirement); - var model = new EditOpenIdApplicationViewModel - { - AllowAuthorizationCodeFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode) && - await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.Code), - - AllowClientCredentialsFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials), - - // Note: the hybrid flow doesn't have a dedicated grant_type but is treated as a combination - // of both the authorization code and implicit grants. As such, to determine whether the hybrid - // flow is enabled, both the authorization code grant and the implicit grant MUST be enabled. - AllowHybridFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode) && - await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Implicit) && - (await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken) || - await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken) || - await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeToken)), - - AllowImplicitFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Implicit) && - (await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.IdToken) || - await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken) || - await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.Token)), - - AllowPasswordFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Password), - AllowRefreshTokenFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.RefreshToken), - AllowLogoutEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Logout), - AllowIntrospectionEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Introspection), - AllowRevocationEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Revocation), - ClientId = await _applicationManager.GetClientIdAsync(application), - ConsentType = await _applicationManager.GetConsentTypeAsync(application), - DisplayName = await _applicationManager.GetDisplayNameAsync(application), - Id = await _applicationManager.GetPhysicalIdAsync(application), - PostLogoutRedirectUris = string.Join(" ", await _applicationManager.GetPostLogoutRedirectUrisAsync(application)), - RedirectUris = string.Join(" ", await _applicationManager.GetRedirectUrisAsync(application)), - Type = await _applicationManager.GetClientTypeAsync(application), - RequireProofKeyForCodeExchange = await HasRequirementAsync(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange) - }; - - var roleService = HttpContext.RequestServices?.GetService(); - if (roleService != null) - { - var roles = await _applicationManager.GetRolesAsync(application); - - foreach (var role in await roleService.GetRoleNamesAsync()) - { - model.RoleEntries.Add(new EditOpenIdApplicationViewModel.RoleEntry - { - Name = role, - Selected = roles.Contains(role, StringComparer.OrdinalIgnoreCase) - }); - } - } - else - { - await _notifier.WarningAsync(H["There are no registered services to provide roles."]); - } + var model = new EditOpenIdApplicationViewModel + { + AllowAuthorizationCodeFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode) && + await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.Code), + + AllowClientCredentialsFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials), + + // Note: the hybrid flow doesn't have a dedicated grant_type but is treated as a combination + // of both the authorization code and implicit grants. As such, to determine whether the hybrid + // flow is enabled, both the authorization code grant and the implicit grant MUST be enabled. + AllowHybridFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode) && + await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Implicit) && + (await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken) || + await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken) || + await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.CodeToken)), + + AllowImplicitFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Implicit) && + (await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.IdToken) || + await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken) || + await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.Token)), + + AllowPasswordFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Password), + AllowRefreshTokenFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.RefreshToken), + AllowLogoutEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Logout), + AllowIntrospectionEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Introspection), + AllowRevocationEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Revocation), + ClientId = await _applicationManager.GetClientIdAsync(application), + ConsentType = await _applicationManager.GetConsentTypeAsync(application), + DisplayName = await _applicationManager.GetDisplayNameAsync(application), + Id = await _applicationManager.GetPhysicalIdAsync(application), + PostLogoutRedirectUris = string.Join(" ", await _applicationManager.GetPostLogoutRedirectUrisAsync(application)), + RedirectUris = string.Join(" ", await _applicationManager.GetRedirectUrisAsync(application)), + Type = await _applicationManager.GetClientTypeAsync(application), + RequireProofKeyForCodeExchange = await HasRequirementAsync(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange) + }; + + var roleService = HttpContext.RequestServices?.GetService(); + if (roleService != null) + { + var roles = await _applicationManager.GetRolesAsync(application); - var permissions = await _applicationManager.GetPermissionsAsync(application); - await foreach (var scope in _scopeManager.ListAsync()) + foreach (var role in await roleService.GetRoleNamesAsync()) { - var scopeName = await _scopeManager.GetNameAsync(scope); - model.ScopeEntries.Add(new EditOpenIdApplicationViewModel.ScopeEntry + model.RoleEntries.Add(new EditOpenIdApplicationViewModel.RoleEntry { - Name = scopeName, - Selected = await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.Prefixes.Scope + scopeName) + Name = role, + Selected = roles.Contains(role, StringComparer.OrdinalIgnoreCase) }); } - - ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); - ViewData["ReturnUrl"] = returnUrl; - return View(model); + } + else + { + await _notifier.WarningAsync(H["There are no registered services to provide roles."]); } - [HttpPost] - public async Task Edit(EditOpenIdApplicationViewModel model, string returnUrl = null) + var permissions = await _applicationManager.GetPermissionsAsync(application); + await foreach (var scope in _scopeManager.ListAsync()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + var scopeName = await _scopeManager.GetNameAsync(scope); + model.ScopeEntries.Add(new EditOpenIdApplicationViewModel.ScopeEntry { - return Forbid(); - } + Name = scopeName, + Selected = await _applicationManager.HasPermissionAsync(application, OpenIddictConstants.Permissions.Prefixes.Scope + scopeName) + }); + } - var application = await _applicationManager.FindByPhysicalIdAsync(model.Id); - if (application == null) - { - return NotFound(); - } + ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - // If the application was a public client and is now a confidential client, ensure a client secret was provided. - if (string.IsNullOrEmpty(model.ClientSecret) && - !string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase) && - await _applicationManager.HasClientTypeAsync(application, OpenIddictConstants.ClientTypes.Public)) - { - ModelState.AddModelError(nameof(model.ClientSecret), S["Setting a new client secret is required."]); - } + [HttpPost] + public async Task Edit(EditOpenIdApplicationViewModel model, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + { + return Forbid(); + } - if (!string.IsNullOrEmpty(model.ClientSecret) && - string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) - { - ModelState.AddModelError(nameof(model.ClientSecret), S["No client secret can be set for public applications."]); - } + var application = await _applicationManager.FindByPhysicalIdAsync(model.Id); + if (application == null) + { + return NotFound(); + } - if (ModelState.IsValid) - { - var other = await _applicationManager.FindByClientIdAsync(model.ClientId); - if (other != null && !string.Equals( - await _applicationManager.GetIdAsync(other), - await _applicationManager.GetIdAsync(application), StringComparison.Ordinal)) - { - ModelState.AddModelError(nameof(model.ClientId), S["The client identifier is already taken by another application."]); - } - } + // If the application was a public client and is now a confidential client, ensure a client secret was provided. + if (string.IsNullOrEmpty(model.ClientSecret) && + !string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase) && + await _applicationManager.HasClientTypeAsync(application, OpenIddictConstants.ClientTypes.Public)) + { + ModelState.AddModelError(nameof(model.ClientSecret), S["Setting a new client secret is required."]); + } - if (!ModelState.IsValid) - { - ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); - ViewData["ReturnUrl"] = returnUrl; - return View(model); - } + if (!string.IsNullOrEmpty(model.ClientSecret) && + string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) + { + ModelState.AddModelError(nameof(model.ClientSecret), S["No client secret can be set for public applications."]); + } - var settings = new OpenIdApplicationSettings() - { - AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, - AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, - AllowHybridFlow = model.AllowHybridFlow, - AllowImplicitFlow = model.AllowImplicitFlow, - AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, - AllowLogoutEndpoint = model.AllowLogoutEndpoint, - AllowPasswordFlow = model.AllowPasswordFlow, - AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, - AllowRevocationEndpoint = model.AllowRevocationEndpoint, - ClientId = model.ClientId, - ClientSecret = model.ClientSecret, - ConsentType = model.ConsentType, - DisplayName = model.DisplayName, - PostLogoutRedirectUris = model.PostLogoutRedirectUris, - RedirectUris = model.RedirectUris, - Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), - Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), - Type = model.Type, - RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange - }; - - await _applicationManager.UpdateDescriptorFromSettings(settings, application); - - if (string.IsNullOrEmpty(returnUrl)) + if (ModelState.IsValid) + { + var other = await _applicationManager.FindByClientIdAsync(model.ClientId); + if (other != null && !string.Equals( + await _applicationManager.GetIdAsync(other), + await _applicationManager.GetIdAsync(application), StringComparison.Ordinal)) { - return RedirectToAction(nameof(Index)); + ModelState.AddModelError(nameof(model.ClientId), S["The client identifier is already taken by another application."]); } + } - return this.LocalRedirect(returnUrl, true); + if (!ModelState.IsValid) + { + ViewData[nameof(OpenIdServerSettings)] = await GetServerSettingsAsync(); + ViewData["ReturnUrl"] = returnUrl; + return View(model); } - [HttpPost] - public async Task Delete(string id) + var settings = new OpenIdApplicationSettings() { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) - { - return Forbid(); - } + AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, + AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, + AllowHybridFlow = model.AllowHybridFlow, + AllowImplicitFlow = model.AllowImplicitFlow, + AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, + AllowLogoutEndpoint = model.AllowLogoutEndpoint, + AllowPasswordFlow = model.AllowPasswordFlow, + AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, + AllowRevocationEndpoint = model.AllowRevocationEndpoint, + ClientId = model.ClientId, + ClientSecret = model.ClientSecret, + ConsentType = model.ConsentType, + DisplayName = model.DisplayName, + PostLogoutRedirectUris = model.PostLogoutRedirectUris, + RedirectUris = model.RedirectUris, + Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), + Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(), + Type = model.Type, + RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange + }; + + await _applicationManager.UpdateDescriptorFromSettings(settings, application); + + if (string.IsNullOrEmpty(returnUrl)) + { + return RedirectToAction(nameof(Index)); + } - var application = await _applicationManager.FindByPhysicalIdAsync(id); - if (application == null) - { - return NotFound(); - } + return this.LocalRedirect(returnUrl, true); + } - await _applicationManager.DeleteAsync(application); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageApplications)) + { + return Forbid(); + } - return RedirectToAction(nameof(Index)); + var application = await _applicationManager.FindByPhysicalIdAsync(id); + if (application == null) + { + return NotFound(); } - private async Task GetServerSettingsAsync() + await _applicationManager.DeleteAsync(application); + + return RedirectToAction(nameof(Index)); + } + + private async Task GetServerSettingsAsync() + { + if (_shellDescriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) { - if (_shellDescriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) + var service = HttpContext.RequestServices.GetRequiredService(); + var settings = await service.GetSettingsAsync(); + if ((await service.ValidateSettingsAsync(settings)).Any(result => result != ValidationResult.Success)) { - var service = HttpContext.RequestServices.GetRequiredService(); - var settings = await service.GetSettingsAsync(); - if ((await service.ValidateSettingsAsync(settings)).Any(result => result != ValidationResult.Success)) - { - await _notifier.WarningAsync(H["OpenID Connect settings are not properly configured."]); - } - - return settings; + await _notifier.WarningAsync(H["OpenID Connect settings are not properly configured."]); } - return null; + return settings; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ScopeController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ScopeController.cs index 4a31540ed34..87991468e92 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ScopeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ScopeController.cs @@ -16,261 +16,260 @@ using OrchardCore.OpenId.Abstractions.Managers; using OrchardCore.OpenId.ViewModels; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +[Feature(OpenIdConstants.Features.Management)] +[Admin("OpenId/Scope/{action}/{id?}", "OpenIdScope{action}")] +public class ScopeController : Controller { - [Feature(OpenIdConstants.Features.Management)] - [Admin("OpenId/Scope/{action}/{id?}", "OpenIdScope{action}")] - public class ScopeController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IOpenIdScopeManager _scopeManager; + private readonly IShapeFactory _shapeFactory; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly ShellDescriptor _shellDescriptor; + private readonly ShellSettings _shellSettings; + private readonly IShellHost _shellHost; + + protected readonly IStringLocalizer S; + + public ScopeController( + IOpenIdScopeManager scopeManager, + IShapeFactory shapeFactory, + IOptions pagerOptions, + IStringLocalizer stringLocalizer, + IAuthorizationService authorizationService, + INotifier notifier, + ShellDescriptor shellDescriptor, + ShellSettings shellSettings, + IShellHost shellHost) + { + _scopeManager = scopeManager; + _shapeFactory = shapeFactory; + _pagerOptions = pagerOptions.Value; + S = stringLocalizer; + _authorizationService = authorizationService; + _notifier = notifier; + _shellDescriptor = shellDescriptor; + _shellSettings = shellSettings; + _shellHost = shellHost; + } + + [Admin("OpenId/Scope", "OpenIdScope")] + public async Task Index(PagerParameters pagerParameters) { - private readonly IAuthorizationService _authorizationService; - private readonly IOpenIdScopeManager _scopeManager; - private readonly IShapeFactory _shapeFactory; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly ShellDescriptor _shellDescriptor; - private readonly ShellSettings _shellSettings; - private readonly IShellHost _shellHost; - - protected readonly IStringLocalizer S; - - public ScopeController( - IOpenIdScopeManager scopeManager, - IShapeFactory shapeFactory, - IOptions pagerOptions, - IStringLocalizer stringLocalizer, - IAuthorizationService authorizationService, - INotifier notifier, - ShellDescriptor shellDescriptor, - ShellSettings shellSettings, - IShellHost shellHost) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) { - _scopeManager = scopeManager; - _shapeFactory = shapeFactory; - _pagerOptions = pagerOptions.Value; - S = stringLocalizer; - _authorizationService = authorizationService; - _notifier = notifier; - _shellDescriptor = shellDescriptor; - _shellSettings = shellSettings; - _shellHost = shellHost; + return Forbid(); } - [Admin("OpenId/Scope", "OpenIdScope")] - public async Task Index(PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var count = await _scopeManager.CountAsync(); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var count = await _scopeManager.CountAsync(); + var model = new OpenIdScopeIndexViewModel + { + Pager = await _shapeFactory.PagerAsync(pager, (int)count), + }; - var model = new OpenIdScopeIndexViewModel + await foreach (var scope in _scopeManager.ListAsync(pager.PageSize, pager.GetStartIndex())) + { + model.Scopes.Add(new OpenIdScopeEntry { - Pager = await _shapeFactory.PagerAsync(pager, (int)count), - }; + Description = await _scopeManager.GetDescriptionAsync(scope), + DisplayName = await _scopeManager.GetDisplayNameAsync(scope), + Id = await _scopeManager.GetPhysicalIdAsync(scope), + Name = await _scopeManager.GetNameAsync(scope) + }); + } - await foreach (var scope in _scopeManager.ListAsync(pager.PageSize, pager.GetStartIndex())) - { - model.Scopes.Add(new OpenIdScopeEntry - { - Description = await _scopeManager.GetDescriptionAsync(scope), - DisplayName = await _scopeManager.GetDisplayNameAsync(scope), - Id = await _scopeManager.GetPhysicalIdAsync(scope), - Name = await _scopeManager.GetNameAsync(scope) - }); - } + return View(model); + } - return View(model); + [HttpGet] + public async Task Create(string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + { + return Forbid(); } - [HttpGet] - public async Task Create(string returnUrl = null) + var model = new CreateOpenIdScopeViewModel(); + + foreach (var tenant in _shellHost.GetAllSettings().Where(s => s.IsRunning())) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + model.Tenants.Add(new CreateOpenIdScopeViewModel.TenantEntry { - return Forbid(); - } + Current = string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal), + Name = tenant.Name + }); + } - var model = new CreateOpenIdScopeViewModel(); + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - foreach (var tenant in _shellHost.GetAllSettings().Where(s => s.IsRunning())) - { - model.Tenants.Add(new CreateOpenIdScopeViewModel.TenantEntry - { - Current = string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal), - Name = tenant.Name - }); - } + [HttpPost] + public async Task Create(CreateOpenIdScopeViewModel model, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + { + return Forbid(); + } + if (await _scopeManager.FindByNameAsync(model.Name) != null) + { + ModelState.AddModelError(nameof(model.Name), S["The name is already taken by another scope."]); + } + + if (!ModelState.IsValid) + { ViewData["ReturnUrl"] = returnUrl; return View(model); } - [HttpPost] - public async Task Create(CreateOpenIdScopeViewModel model, string returnUrl = null) + var descriptor = new OpenIdScopeDescriptor { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) - { - return Forbid(); - } + Description = model.Description, + DisplayName = model.DisplayName, + Name = model.Name + }; - if (await _scopeManager.FindByNameAsync(model.Name) != null) - { - ModelState.AddModelError(nameof(model.Name), S["The name is already taken by another scope."]); - } - - if (!ModelState.IsValid) - { - ViewData["ReturnUrl"] = returnUrl; - return View(model); - } + if (!string.IsNullOrEmpty(model.Resources)) + { + descriptor.Resources.UnionWith(model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + } - var descriptor = new OpenIdScopeDescriptor - { - Description = model.Description, - DisplayName = model.DisplayName, - Name = model.Name - }; + descriptor.Resources.UnionWith(model.Tenants + .Where(tenant => tenant.Selected) + .Where(tenant => !string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal)) + .Select(tenant => OpenIdConstants.Prefixes.Tenant + tenant.Name)); - if (!string.IsNullOrEmpty(model.Resources)) - { - descriptor.Resources.UnionWith(model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - } - - descriptor.Resources.UnionWith(model.Tenants - .Where(tenant => tenant.Selected) - .Where(tenant => !string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal)) - .Select(tenant => OpenIdConstants.Prefixes.Tenant + tenant.Name)); + await _scopeManager.CreateAsync(descriptor); - await _scopeManager.CreateAsync(descriptor); + if (string.IsNullOrEmpty(returnUrl)) + { + return RedirectToAction("Index"); + } - if (string.IsNullOrEmpty(returnUrl)) - { - return RedirectToAction("Index"); - } + return this.LocalRedirect(returnUrl, true); + } - return this.LocalRedirect(returnUrl, true); + public async Task Edit(string id, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + { + return Forbid(); } - public async Task Edit(string id, string returnUrl = null) + var scope = await _scopeManager.FindByPhysicalIdAsync(id); + if (scope == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) - { - return Forbid(); - } - - var scope = await _scopeManager.FindByPhysicalIdAsync(id); - if (scope == null) - { - return NotFound(); - } + return NotFound(); + } - var model = new EditOpenIdScopeViewModel - { - Description = await _scopeManager.GetDescriptionAsync(scope), - DisplayName = await _scopeManager.GetDisplayNameAsync(scope), - Id = await _scopeManager.GetPhysicalIdAsync(scope), - Name = await _scopeManager.GetNameAsync(scope) - }; + var model = new EditOpenIdScopeViewModel + { + Description = await _scopeManager.GetDescriptionAsync(scope), + DisplayName = await _scopeManager.GetDisplayNameAsync(scope), + Id = await _scopeManager.GetPhysicalIdAsync(scope), + Name = await _scopeManager.GetNameAsync(scope) + }; - var resources = await _scopeManager.GetResourcesAsync(scope); + var resources = await _scopeManager.GetResourcesAsync(scope); - model.Resources = string.Join(" ", - from resource in resources - where !string.IsNullOrEmpty(resource) && !resource.StartsWith(OpenIdConstants.Prefixes.Tenant, StringComparison.Ordinal) - select resource); + model.Resources = string.Join(" ", + from resource in resources + where !string.IsNullOrEmpty(resource) && !resource.StartsWith(OpenIdConstants.Prefixes.Tenant, StringComparison.Ordinal) + select resource); - foreach (var tenant in _shellHost.GetAllSettings().Where(s => s.IsRunning())) + foreach (var tenant in _shellHost.GetAllSettings().Where(s => s.IsRunning())) + { + model.Tenants.Add(new EditOpenIdScopeViewModel.TenantEntry { - model.Tenants.Add(new EditOpenIdScopeViewModel.TenantEntry - { - Current = string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal), - Name = tenant.Name, - Selected = resources.Contains(OpenIdConstants.Prefixes.Tenant + tenant.Name) - }); - } + Current = string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal), + Name = tenant.Name, + Selected = resources.Contains(OpenIdConstants.Prefixes.Tenant + tenant.Name) + }); + } - ViewData["ReturnUrl"] = returnUrl; - return View(model); + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } + + [HttpPost] + public async Task Edit(EditOpenIdScopeViewModel model, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + { + return Forbid(); } - [HttpPost] - public async Task Edit(EditOpenIdScopeViewModel model, string returnUrl = null) + var scope = await _scopeManager.FindByPhysicalIdAsync(model.Id); + if (scope == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) - { - return Forbid(); - } + return NotFound(); + } - var scope = await _scopeManager.FindByPhysicalIdAsync(model.Id); - if (scope == null) + if (ModelState.IsValid) + { + var other = await _scopeManager.FindByNameAsync(model.Name); + if (other != null && !string.Equals(await _scopeManager.GetIdAsync(other), await _scopeManager.GetIdAsync(scope), StringComparison.Ordinal)) { - return NotFound(); + ModelState.AddModelError(nameof(model.Name), S["The name is already taken by another scope."]); } + } - if (ModelState.IsValid) - { - var other = await _scopeManager.FindByNameAsync(model.Name); - if (other != null && !string.Equals(await _scopeManager.GetIdAsync(other), await _scopeManager.GetIdAsync(scope), StringComparison.Ordinal)) - { - ModelState.AddModelError(nameof(model.Name), S["The name is already taken by another scope."]); - } - } + if (!ModelState.IsValid) + { + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - if (!ModelState.IsValid) - { - ViewData["ReturnUrl"] = returnUrl; - return View(model); - } + var descriptor = new OpenIdScopeDescriptor(); + await _scopeManager.PopulateAsync(descriptor, scope); - var descriptor = new OpenIdScopeDescriptor(); - await _scopeManager.PopulateAsync(descriptor, scope); + descriptor.Description = model.Description; + descriptor.DisplayName = model.DisplayName; + descriptor.Name = model.Name; - descriptor.Description = model.Description; - descriptor.DisplayName = model.DisplayName; - descriptor.Name = model.Name; + descriptor.Resources.Clear(); - descriptor.Resources.Clear(); + if (!string.IsNullOrEmpty(model.Resources)) + { + descriptor.Resources.UnionWith(model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + } - if (!string.IsNullOrEmpty(model.Resources)) - { - descriptor.Resources.UnionWith(model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - } + descriptor.Resources.UnionWith(model.Tenants + .Where(tenant => tenant.Selected) + .Where(tenant => !string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal)) + .Select(tenant => OpenIdConstants.Prefixes.Tenant + tenant.Name)); - descriptor.Resources.UnionWith(model.Tenants - .Where(tenant => tenant.Selected) - .Where(tenant => !string.Equals(tenant.Name, _shellSettings.Name, StringComparison.Ordinal)) - .Select(tenant => OpenIdConstants.Prefixes.Tenant + tenant.Name)); + await _scopeManager.UpdateAsync(scope, descriptor); - await _scopeManager.UpdateAsync(scope, descriptor); + if (string.IsNullOrEmpty(returnUrl)) + { + return RedirectToAction("Index"); + } - if (string.IsNullOrEmpty(returnUrl)) - { - return RedirectToAction("Index"); - } + return this.LocalRedirect(returnUrl, true); + } - return this.LocalRedirect(returnUrl, true); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string id) + var scope = await _scopeManager.FindByPhysicalIdAsync(id); + if (scope == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageScopes)) - { - return Forbid(); - } - - var scope = await _scopeManager.FindByPhysicalIdAsync(id); - if (scope == null) - { - return NotFound(); - } + return NotFound(); + } - await _scopeManager.DeleteAsync(scope); + await _scopeManager.DeleteAsync(scope); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ServerConfigurationController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ServerConfigurationController.cs index f6c408a2b51..a967dba6577 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ServerConfigurationController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ServerConfigurationController.cs @@ -13,92 +13,91 @@ using OrchardCore.OpenId.Services; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +[Admin, Feature(OpenIdConstants.Features.Server)] +public class ServerConfigurationController : Controller { - [Admin, Feature(OpenIdConstants.Features.Server)] - public class ServerConfigurationController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; + private readonly IOpenIdServerService _serverService; + private readonly IDisplayManager _serverSettingsDisplayManager; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IHtmlLocalizer H; + + public ServerConfigurationController( + IAuthorizationService authorizationService, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IOpenIdServerService serverService, + IDisplayManager serverSettingsDisplayManager, + IShellHost shellHost, + ShellSettings shellSettings, + IUpdateModelAccessor updateModelAccessor) { - private readonly IAuthorizationService _authorizationService; - private readonly INotifier _notifier; - private readonly IOpenIdServerService _serverService; - private readonly IDisplayManager _serverSettingsDisplayManager; - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IHtmlLocalizer H; + _authorizationService = authorizationService; + H = htmlLocalizer; + _notifier = notifier; + _serverService = serverService; + _serverSettingsDisplayManager = serverSettingsDisplayManager; + _shellHost = shellHost; + _shellSettings = shellSettings; + _updateModelAccessor = updateModelAccessor; + } - public ServerConfigurationController( - IAuthorizationService authorizationService, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IOpenIdServerService serverService, - IDisplayManager serverSettingsDisplayManager, - IShellHost shellHost, - ShellSettings shellSettings, - IUpdateModelAccessor updateModelAccessor) + [Admin("OpenId/ServerConfiguration", "OpenIdServerConfiguration")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageServerSettings)) { - _authorizationService = authorizationService; - H = htmlLocalizer; - _notifier = notifier; - _serverService = serverService; - _serverSettingsDisplayManager = serverSettingsDisplayManager; - _shellHost = shellHost; - _shellSettings = shellSettings; - _updateModelAccessor = updateModelAccessor; + return Forbid(); } - [Admin("OpenId/ServerConfiguration", "OpenIdServerConfiguration")] - public async Task Index() + var settings = await _serverService.GetSettingsAsync(); + var shape = await _serverSettingsDisplayManager.BuildEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + + return View(shape); + } + + [HttpPost] + [ActionName(nameof(Index))] + public async Task IndexPost() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageServerSettings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageServerSettings)) - { - return Forbid(); - } + return Forbid(); + } - var settings = await _serverService.GetSettingsAsync(); - var shape = await _serverSettingsDisplayManager.BuildEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var settings = await _serverService.GetSettingsAsync(); + var shape = await _serverSettingsDisplayManager.UpdateEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + if (!ModelState.IsValid) + { return View(shape); } - [HttpPost] - [ActionName(nameof(Index))] - public async Task IndexPost() + foreach (var result in await _serverService.ValidateSettingsAsync(settings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageServerSettings)) - { - return Forbid(); - } - - var settings = await _serverService.GetSettingsAsync(); - var shape = await _serverSettingsDisplayManager.UpdateEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - - if (!ModelState.IsValid) + if (result != ValidationResult.Success) { - return View(shape); - } - - foreach (var result in await _serverService.ValidateSettingsAsync(settings)) - { - if (result != ValidationResult.Success) - { - var key = result.MemberNames.FirstOrDefault() ?? string.Empty; - ModelState.AddModelError(key, result.ErrorMessage); - } + var key = result.MemberNames.FirstOrDefault() ?? string.Empty; + ModelState.AddModelError(key, result.ErrorMessage); } + } - if (!ModelState.IsValid) - { - return View(shape); - } + if (!ModelState.IsValid) + { + return View(shape); + } - await _serverService.UpdateSettingsAsync(settings); + await _serverService.UpdateSettingsAsync(settings); - await _notifier.SuccessAsync(H["OpenID server configuration successfully updated."]); + await _notifier.SuccessAsync(H["OpenID server configuration successfully updated."]); - await _shellHost.ReleaseShellContextAsync(_shellSettings); + await _shellHost.ReleaseShellContextAsync(_shellSettings); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/UserInfoController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/UserInfoController.cs index af7a118ef9c..eeace0f93b8 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/UserInfoController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/UserInfoController.cs @@ -12,154 +12,153 @@ using OrchardCore.Modules; using static OpenIddict.Abstractions.OpenIddictConstants; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +// Note: the error descriptions used in this controller are deliberately not localized as +// the OAuth 2.0 specification only allows select US-ASCII characters in error_description. +[Feature(OpenIdConstants.Features.Server), SkipStatusCodePages] +public class UserInfoController : Controller { - // Note: the error descriptions used in this controller are deliberately not localized as - // the OAuth 2.0 specification only allows select US-ASCII characters in error_description. - [Feature(OpenIdConstants.Features.Server), SkipStatusCodePages] - public class UserInfoController : Controller + // GET/POST: /connect/userinfo + [AcceptVerbs("GET", "POST")] + [IgnoreAntiforgeryToken] + [Produces("application/json")] + public async Task Me() { - // GET/POST: /connect/userinfo - [AcceptVerbs("GET", "POST")] - [IgnoreAntiforgeryToken] - [Produces("application/json")] - public async Task Me() + // Warning: this action is decorated with IgnoreAntiforgeryTokenAttribute to override + // the global antiforgery token validation policy applied by the MVC modules stack, + // which is required for this stateless OpenID userinfo endpoint to work correctly. + // To prevent effective CSRF/session fixation attacks, this action MUST NOT return + // an authentication cookie or try to establish an ASP.NET Core user session. + + var request = HttpContext.GetOpenIddictServerRequest(); + if (request == null) + { + return NotFound(); + } + + // Note: this controller doesn't use [Authorize] to prevent MVC from throwing + // an exception if the OpenIddict server handler was not registered (e.g because the + // OpenID server feature was not enabled or because the configuration was invalid). + var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme))?.Principal; + if (principal == null) + { + return Challenge(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + // Ensure the access token represents a user and not an application. + var type = principal.FindFirst(OpenIdConstants.Claims.EntityType)?.Value; + if (!string.Equals(type, OpenIdConstants.EntityTypes.User, StringComparison.Ordinal)) { - // Warning: this action is decorated with IgnoreAntiforgeryTokenAttribute to override - // the global antiforgery token validation policy applied by the MVC modules stack, - // which is required for this stateless OpenID userinfo endpoint to work correctly. - // To prevent effective CSRF/session fixation attacks, this action MUST NOT return - // an authentication cookie or try to establish an ASP.NET Core user session. - - var request = HttpContext.GetOpenIddictServerRequest(); - if (request == null) + return Forbid(new AuthenticationProperties(new Dictionary { - return NotFound(); + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = + "The userinfo endpoint can only be used with access tokens representing users." + }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + var claims = new Dictionary(); + + if (principal.HasScope(Scopes.Profile)) + { + var preferredUsername = principal.FindFirst(Claims.PreferredUsername)?.Value; + if (!string.IsNullOrEmpty(preferredUsername)) + { + claims[Claims.PreferredUsername] = preferredUsername; } - // Note: this controller doesn't use [Authorize] to prevent MVC from throwing - // an exception if the OpenIddict server handler was not registered (e.g because the - // OpenID server feature was not enabled or because the configuration was invalid). - var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme))?.Principal; - if (principal == null) + var name = principal.FindFirst(Claims.Name)?.Value ?? principal.FindFirst(ClaimTypes.Name)?.Value; + if (!string.IsNullOrEmpty(name)) { - return Challenge(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + claims[Claims.Name] = name; } - // Ensure the access token represents a user and not an application. - var type = principal.FindFirst(OpenIdConstants.Claims.EntityType)?.Value; - if (!string.Equals(type, OpenIdConstants.EntityTypes.User, StringComparison.Ordinal)) + var familyName = principal.FindFirst(Claims.FamilyName)?.Value ?? principal.FindFirst(ClaimTypes.Surname)?.Value; + if (!string.IsNullOrEmpty(familyName)) { - return Forbid(new AuthenticationProperties(new Dictionary - { - [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = - "The userinfo endpoint can only be used with access tokens representing users." - }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + claims[Claims.FamilyName] = familyName; } - var claims = new Dictionary(); + var givenName = principal.FindFirst(Claims.GivenName)?.Value ?? principal.FindFirst(ClaimTypes.GivenName)?.Value; + if (!string.IsNullOrEmpty(givenName)) + { + claims[Claims.GivenName] = givenName; + } - if (principal.HasScope(Scopes.Profile)) + var middleName = principal.FindFirst(Claims.MiddleName)?.Value; + if (!string.IsNullOrEmpty(middleName)) { - var preferredUsername = principal.FindFirst(Claims.PreferredUsername)?.Value; - if (!string.IsNullOrEmpty(preferredUsername)) - { - claims[Claims.PreferredUsername] = preferredUsername; - } + claims[Claims.MiddleName] = middleName; + } - var name = principal.FindFirst(Claims.Name)?.Value ?? principal.FindFirst(ClaimTypes.Name)?.Value; - if (!string.IsNullOrEmpty(name)) - { - claims[Claims.Name] = name; - } + var picture = principal.FindFirst(Claims.Picture)?.Value; + if (!string.IsNullOrEmpty(picture)) + { + claims[Claims.Picture] = picture; + } - var familyName = principal.FindFirst(Claims.FamilyName)?.Value ?? principal.FindFirst(ClaimTypes.Surname)?.Value; - if (!string.IsNullOrEmpty(familyName)) - { - claims[Claims.FamilyName] = familyName; - } + var updatedAtClaimValue = principal.FindFirst(Claims.UpdatedAt)?.Value; + if (!string.IsNullOrEmpty(updatedAtClaimValue)) + { + claims[Claims.UpdatedAt] = long.Parse(updatedAtClaimValue, CultureInfo.InvariantCulture); + } + } - var givenName = principal.FindFirst(Claims.GivenName)?.Value ?? principal.FindFirst(ClaimTypes.GivenName)?.Value; - if (!string.IsNullOrEmpty(givenName)) - { - claims[Claims.GivenName] = givenName; - } + // Note: the "sub" claim is a mandatory claim and must be included in the JSON response. + claims[Claims.Subject] = principal.GetUserIdentifier(); - var middleName = principal.FindFirst(Claims.MiddleName)?.Value; - if (!string.IsNullOrEmpty(middleName)) - { - claims[Claims.MiddleName] = middleName; - } + if (principal.HasScope(Scopes.Email)) + { + var address = principal.FindFirst(Claims.Email)?.Value ?? principal.FindFirst(ClaimTypes.Email)?.Value; - var picture = principal.FindFirst(Claims.Picture)?.Value; - if (!string.IsNullOrEmpty(picture)) - { - claims[Claims.Picture] = picture; - } + if (!string.IsNullOrEmpty(address)) + { + claims[Claims.Email] = address; - var updatedAtClaimValue = principal.FindFirst(Claims.UpdatedAt)?.Value; - if (!string.IsNullOrEmpty(updatedAtClaimValue)) + var status = principal.FindFirst(Claims.EmailVerified)?.Value; + if (!string.IsNullOrEmpty(status)) { - claims[Claims.UpdatedAt] = long.Parse(updatedAtClaimValue, CultureInfo.InvariantCulture); + claims[Claims.EmailVerified] = bool.Parse(status); } } + } - // Note: the "sub" claim is a mandatory claim and must be included in the JSON response. - claims[Claims.Subject] = principal.GetUserIdentifier(); + if (principal.HasScope(Scopes.Phone)) + { + var phone = principal.FindFirst(Claims.PhoneNumber)?.Value ?? + principal.FindFirst(ClaimTypes.MobilePhone)?.Value ?? + principal.FindFirst(ClaimTypes.HomePhone)?.Value ?? + principal.FindFirst(ClaimTypes.OtherPhone)?.Value; - if (principal.HasScope(Scopes.Email)) + if (!string.IsNullOrEmpty(phone)) { - var address = principal.FindFirst(Claims.Email)?.Value ?? principal.FindFirst(ClaimTypes.Email)?.Value; + claims[Claims.PhoneNumber] = phone; - if (!string.IsNullOrEmpty(address)) + var status = principal.FindFirst(Claims.PhoneNumberVerified)?.Value; + if (!string.IsNullOrEmpty(status)) { - claims[Claims.Email] = address; - - var status = principal.FindFirst(Claims.EmailVerified)?.Value; - if (!string.IsNullOrEmpty(status)) - { - claims[Claims.EmailVerified] = bool.Parse(status); - } + claims[Claims.PhoneNumberVerified] = bool.Parse(status); } } + } - if (principal.HasScope(Scopes.Phone)) - { - var phone = principal.FindFirst(Claims.PhoneNumber)?.Value ?? - principal.FindFirst(ClaimTypes.MobilePhone)?.Value ?? - principal.FindFirst(ClaimTypes.HomePhone)?.Value ?? - principal.FindFirst(ClaimTypes.OtherPhone)?.Value; - - if (!string.IsNullOrEmpty(phone)) - { - claims[Claims.PhoneNumber] = phone; - - var status = principal.FindFirst(Claims.PhoneNumberVerified)?.Value; - if (!string.IsNullOrEmpty(status)) - { - claims[Claims.PhoneNumberVerified] = bool.Parse(status); - } - } - } + if (principal.HasScope(Scopes.Roles)) + { + var roles = principal.FindAll(Claims.Role) + .Concat(principal.FindAll(ClaimTypes.Role)) + .Select(claim => claim.Value) + .ToArray(); - if (principal.HasScope(Scopes.Roles)) + if (roles.Length != 0) { - var roles = principal.FindAll(Claims.Role) - .Concat(principal.FindAll(ClaimTypes.Role)) - .Select(claim => claim.Value) - .ToArray(); - - if (roles.Length != 0) - { - claims["roles"] = roles; - } + claims["roles"] = roles; } + } - // Note: the complete list of standard claims supported by the OpenID Connect specification - // can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims + // Note: the complete list of standard claims supported by the OpenID Connect specification + // can be found here: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims - return Ok(claims); - } + return Ok(claims); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ValidationConfigurationController.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ValidationConfigurationController.cs index 7935372dea5..bce962d9510 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ValidationConfigurationController.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Controllers/ValidationConfigurationController.cs @@ -13,92 +13,91 @@ using OrchardCore.OpenId.Services; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Controllers +namespace OrchardCore.OpenId.Controllers; + +[Admin, Feature(OpenIdConstants.Features.Validation)] +public class ValidationConfigurationController : Controller { - [Admin, Feature(OpenIdConstants.Features.Validation)] - public class ValidationConfigurationController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; + private readonly IOpenIdValidationService _validationService; + private readonly IDisplayManager _validationSettingsDisplayManager; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IHtmlLocalizer H; + + public ValidationConfigurationController( + IAuthorizationService authorizationService, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IOpenIdValidationService validationService, + IDisplayManager validationSettingsDisplayManager, + IShellHost shellHost, + ShellSettings shellSettings, + IUpdateModelAccessor updateModelAccessor) { - private readonly IAuthorizationService _authorizationService; - private readonly INotifier _notifier; - private readonly IOpenIdValidationService _validationService; - private readonly IDisplayManager _validationSettingsDisplayManager; - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IHtmlLocalizer H; + _authorizationService = authorizationService; + H = htmlLocalizer; + _notifier = notifier; + _validationService = validationService; + _validationSettingsDisplayManager = validationSettingsDisplayManager; + _shellHost = shellHost; + _shellSettings = shellSettings; + _updateModelAccessor = updateModelAccessor; + } - public ValidationConfigurationController( - IAuthorizationService authorizationService, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IOpenIdValidationService validationService, - IDisplayManager validationSettingsDisplayManager, - IShellHost shellHost, - ShellSettings shellSettings, - IUpdateModelAccessor updateModelAccessor) + [Admin("OpenId/ValidationConfiguration", "OpenIdValidationConfiguration")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageValidationSettings)) { - _authorizationService = authorizationService; - H = htmlLocalizer; - _notifier = notifier; - _validationService = validationService; - _validationSettingsDisplayManager = validationSettingsDisplayManager; - _shellHost = shellHost; - _shellSettings = shellSettings; - _updateModelAccessor = updateModelAccessor; + return Forbid(); } - [Admin("OpenId/ValidationConfiguration", "OpenIdValidationConfiguration")] - public async Task Index() + var settings = await _validationService.GetSettingsAsync(); + var shape = await _validationSettingsDisplayManager.BuildEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + + return View(shape); + } + + [HttpPost] + [ActionName(nameof(Index))] + public async Task IndexPost() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageValidationSettings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageValidationSettings)) - { - return Forbid(); - } + return Forbid(); + } - var settings = await _validationService.GetSettingsAsync(); - var shape = await _validationSettingsDisplayManager.BuildEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var settings = await _validationService.GetSettingsAsync(); + var shape = await _validationSettingsDisplayManager.UpdateEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + if (!ModelState.IsValid) + { return View(shape); } - [HttpPost] - [ActionName(nameof(Index))] - public async Task IndexPost() + foreach (var result in await _validationService.ValidateSettingsAsync(settings)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageValidationSettings)) - { - return Forbid(); - } - - var settings = await _validationService.GetSettingsAsync(); - var shape = await _validationSettingsDisplayManager.UpdateEditorAsync(settings, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - - if (!ModelState.IsValid) + if (result != ValidationResult.Success) { - return View(shape); - } - - foreach (var result in await _validationService.ValidateSettingsAsync(settings)) - { - if (result != ValidationResult.Success) - { - var key = result.MemberNames.FirstOrDefault() ?? string.Empty; - ModelState.AddModelError(key, result.ErrorMessage); - } + var key = result.MemberNames.FirstOrDefault() ?? string.Empty; + ModelState.AddModelError(key, result.ErrorMessage); } + } - if (!ModelState.IsValid) - { - return View(shape); - } + if (!ModelState.IsValid) + { + return View(shape); + } - await _validationService.UpdateSettingsAsync(settings); + await _validationService.UpdateSettingsAsync(settings); - await _notifier.SuccessAsync(H["OpenID validation configuration successfully updated."]); + await _notifier.SuccessAsync(H["OpenID validation configuration successfully updated."]); - await _shellHost.ReleaseShellContextAsync(_shellSettings); + await _shellHost.ReleaseShellContextAsync(_shellSettings); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentSource.cs index a3474788075..a5569891bd7 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentSource.cs @@ -5,74 +5,73 @@ using OrchardCore.OpenId.Services; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +public class OpenIdServerDeploymentSource : IDeploymentSource { - public class OpenIdServerDeploymentSource : IDeploymentSource + private readonly IOpenIdServerService _openIdServerService; + + public OpenIdServerDeploymentSource(IOpenIdServerService openIdServerService) { - private readonly IOpenIdServerService _openIdServerService; + _openIdServerService = openIdServerService; + } - public OpenIdServerDeploymentSource(IOpenIdServerService openIdServerService) - { - _openIdServerService = openIdServerService; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var openIdServerStep = step as OpenIdServerDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (openIdServerStep == null) { - var openIdServerStep = step as OpenIdServerDeploymentStep; - - if (openIdServerStep == null) - { - return; - } + return; + } - var settings = await _openIdServerService - .GetSettingsAsync(); + var settings = await _openIdServerService + .GetSettingsAsync(); - var settingsModel = new OpenIdServerSettingsStepModel - { - AccessTokenFormat = settings.AccessTokenFormat, - Authority = settings.Authority?.AbsoluteUri, + var settingsModel = new OpenIdServerSettingsStepModel + { + AccessTokenFormat = settings.AccessTokenFormat, + Authority = settings.Authority?.AbsoluteUri, - EncryptionCertificateStoreLocation = settings.EncryptionCertificateStoreLocation, - EncryptionCertificateStoreName = settings.EncryptionCertificateStoreName, - EncryptionCertificateThumbprint = settings.EncryptionCertificateThumbprint, + EncryptionCertificateStoreLocation = settings.EncryptionCertificateStoreLocation, + EncryptionCertificateStoreName = settings.EncryptionCertificateStoreName, + EncryptionCertificateThumbprint = settings.EncryptionCertificateThumbprint, - SigningCertificateStoreLocation = settings.SigningCertificateStoreLocation, - SigningCertificateStoreName = settings.SigningCertificateStoreName, - SigningCertificateThumbprint = settings.SigningCertificateThumbprint, + SigningCertificateStoreLocation = settings.SigningCertificateStoreLocation, + SigningCertificateStoreName = settings.SigningCertificateStoreName, + SigningCertificateThumbprint = settings.SigningCertificateThumbprint, - // The recipe step only reads these flags, and uses constants for the paths. - // Conversely, we export true for endpoints with a path, false for those without. - EnableAuthorizationEndpoint = !string.IsNullOrWhiteSpace(settings.AuthorizationEndpointPath), - EnableLogoutEndpoint = !string.IsNullOrWhiteSpace(settings.LogoutEndpointPath), - EnableTokenEndpoint = !string.IsNullOrWhiteSpace(settings.TokenEndpointPath), - EnableUserInfoEndpoint = !string.IsNullOrWhiteSpace(settings.UserinfoEndpointPath), - EnableIntrospectionEndpoint = !string.IsNullOrWhiteSpace(settings.IntrospectionEndpointPath), - EnableRevocationEndpoint = !string.IsNullOrWhiteSpace(settings.RevocationEndpointPath), + // The recipe step only reads these flags, and uses constants for the paths. + // Conversely, we export true for endpoints with a path, false for those without. + EnableAuthorizationEndpoint = !string.IsNullOrWhiteSpace(settings.AuthorizationEndpointPath), + EnableLogoutEndpoint = !string.IsNullOrWhiteSpace(settings.LogoutEndpointPath), + EnableTokenEndpoint = !string.IsNullOrWhiteSpace(settings.TokenEndpointPath), + EnableUserInfoEndpoint = !string.IsNullOrWhiteSpace(settings.UserinfoEndpointPath), + EnableIntrospectionEndpoint = !string.IsNullOrWhiteSpace(settings.IntrospectionEndpointPath), + EnableRevocationEndpoint = !string.IsNullOrWhiteSpace(settings.RevocationEndpointPath), - AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow, - AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow, - AllowHybridFlow = settings.AllowHybridFlow, - AllowImplicitFlow = settings.AllowImplicitFlow, - AllowPasswordFlow = settings.AllowPasswordFlow, - AllowRefreshTokenFlow = settings.AllowRefreshTokenFlow, + AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow, + AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow, + AllowHybridFlow = settings.AllowHybridFlow, + AllowImplicitFlow = settings.AllowImplicitFlow, + AllowPasswordFlow = settings.AllowPasswordFlow, + AllowRefreshTokenFlow = settings.AllowRefreshTokenFlow, - DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption, - DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens, - UseReferenceAccessTokens = settings.UseReferenceAccessTokens, - RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange, - }; + DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption, + DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens, + UseReferenceAccessTokens = settings.UseReferenceAccessTokens, + RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange, + }; - // Use nameof(OpenIdServerSettings) as name, - // to match the recipe step. - var obj = new JsonObject - { - ["name"] = nameof(OpenIdServerSettings), - }; + // Use nameof(OpenIdServerSettings) as name, + // to match the recipe step. + var obj = new JsonObject + { + ["name"] = nameof(OpenIdServerSettings), + }; - obj.Merge(JObject.FromObject(settingsModel)); + obj.Merge(JObject.FromObject(settingsModel)); - result.Steps.Add(obj); - } + result.Steps.Add(obj); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStep.cs index 348e474febe..53fc76daa00 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +/// +/// Adds Open ID settings to a . +/// +public class OpenIdServerDeploymentStep : DeploymentStep { - /// - /// Adds Open ID settings to a . - /// - public class OpenIdServerDeploymentStep : DeploymentStep + public OpenIdServerDeploymentStep() { - public OpenIdServerDeploymentStep() - { - Name = "OpenID Server"; - } + Name = "OpenID Server"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStepDriver.cs index fc8862fd1e4..9b9b4a25849 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdServerDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +public sealed class OpenIdServerDeploymentStepDriver : DisplayDriver { - public sealed class OpenIdServerDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(OpenIdServerDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(OpenIdServerDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("OpenIdServerDeploymentStep_Summary", step).Location("Summary", "Content"), - View("OpenIdServerDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("OpenIdServerDeploymentStep_Summary", step).Location("Summary", "Content"), + View("OpenIdServerDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(OpenIdServerDeploymentStep step, BuildEditorContext context) - { - return View("OpenIdServerDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(OpenIdServerDeploymentStep step, BuildEditorContext context) + { + return View("OpenIdServerDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentSource.cs index 66bd3305445..70b5524073f 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentSource.cs @@ -4,35 +4,34 @@ using OrchardCore.OpenId.Services; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +public class OpenIdValidationDeploymentSource : IDeploymentSource { - public class OpenIdValidationDeploymentSource : IDeploymentSource + private readonly IOpenIdValidationService _openIdValidationService; + + public OpenIdValidationDeploymentSource(IOpenIdValidationService openIdValidationService) { - private readonly IOpenIdValidationService _openIdValidationService; + _openIdValidationService = openIdValidationService; + } - public OpenIdValidationDeploymentSource(IOpenIdValidationService openIdValidationService) - { - _openIdValidationService = openIdValidationService; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var openIdValidationStep = step as OpenIdValidationDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (openIdValidationStep == null) { - var openIdValidationStep = step as OpenIdValidationDeploymentStep; - - if (openIdValidationStep == null) - { - return; - } + return; + } - var validationSettings = await _openIdValidationService.GetSettingsAsync(); + var validationSettings = await _openIdValidationService.GetSettingsAsync(); - // The 'name' property should match the related recipe step name. - var jObject = new JsonObject { ["name"] = nameof(OpenIdValidationSettings) }; + // The 'name' property should match the related recipe step name. + var jObject = new JsonObject { ["name"] = nameof(OpenIdValidationSettings) }; - // Merge settings as the recipe step doesn't use a child property. - jObject.Merge(JObject.FromObject(validationSettings)); + // Merge settings as the recipe step doesn't use a child property. + jObject.Merge(JObject.FromObject(validationSettings)); - result.Steps.Add(jObject); - } + result.Steps.Add(jObject); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStep.cs index d5b51715d87..729abc83a55 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +/// +/// Adds Open ID settings to a . +/// +public class OpenIdValidationDeploymentStep : DeploymentStep { - /// - /// Adds Open ID settings to a . - /// - public class OpenIdValidationDeploymentStep : DeploymentStep + public OpenIdValidationDeploymentStep() { - public OpenIdValidationDeploymentStep() - { - Name = "OpenID Validation"; - } + Name = "OpenID Validation"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStepDriver.cs index 43f8b0e3f16..2fc99b8e6a5 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Deployment/OpenIdValidationDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.OpenId.Deployment +namespace OrchardCore.OpenId.Deployment; + +public sealed class OpenIdValidationDeploymentStepDriver : DisplayDriver { - public sealed class OpenIdValidationDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(OpenIdValidationDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(OpenIdValidationDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("OpenIdValidationDeploymentStep_Summary", step).Location("Summary", "Content"), - View("OpenIdValidationDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("OpenIdValidationDeploymentStep_Summary", step).Location("Summary", "Content"), + View("OpenIdValidationDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(OpenIdValidationDeploymentStep step, BuildEditorContext context) - { - return View("OpenIdValidationDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(OpenIdValidationDeploymentStep step, BuildEditorContext context) + { + return View("OpenIdValidationDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdClientSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdClientSettingsDisplayDriver.cs index b03b125da35..eb5193950dd 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdClientSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdClientSettingsDisplayDriver.cs @@ -18,189 +18,188 @@ using OrchardCore.OpenId.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.OpenId.Drivers +namespace OrchardCore.OpenId.Drivers; + +public sealed class OpenIdClientSettingsDisplayDriver : SiteDisplayDriver { - public sealed class OpenIdClientSettingsDisplayDriver : SiteDisplayDriver + private static readonly char[] _separator = [' ', ',']; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IAuthorizationService _authorizationService; + private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IOpenIdClientService _clientService; + + internal readonly IStringLocalizer S; + + protected override string SettingsGroupId + => "OrchardCore.OpenId.Client"; + + public OpenIdClientSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IAuthorizationService authorizationService, + IDataProtectionProvider dataProtectionProvider, + IOpenIdClientService clientService, + IHttpContextAccessor httpContextAccessor, + IStringLocalizer stringLocalizer) { - private static readonly char[] _separator = [' ', ',']; - - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IAuthorizationService _authorizationService; - private readonly IDataProtectionProvider _dataProtectionProvider; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IOpenIdClientService _clientService; - - internal readonly IStringLocalizer S; - - protected override string SettingsGroupId - => "OrchardCore.OpenId.Client"; - - public OpenIdClientSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IAuthorizationService authorizationService, - IDataProtectionProvider dataProtectionProvider, - IOpenIdClientService clientService, - IHttpContextAccessor httpContextAccessor, - IStringLocalizer stringLocalizer) - { - _shellReleaseManager = shellReleaseManager; - _authorizationService = authorizationService; - _dataProtectionProvider = dataProtectionProvider; - _clientService = clientService; - _httpContextAccessor = httpContextAccessor; - S = stringLocalizer; - } + _shellReleaseManager = shellReleaseManager; + _authorizationService = authorizationService; + _dataProtectionProvider = dataProtectionProvider; + _clientService = clientService; + _httpContextAccessor = httpContextAccessor; + S = stringLocalizer; + } - public override async Task EditAsync(ISite site, OpenIdClientSettings settings, BuildEditorContext context) + public override async Task EditAsync(ISite site, OpenIdClientSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageClientSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageClientSettings)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); - - return Initialize("OpenIdClientSettings_Edit", model => - { - model.DisplayName = settings.DisplayName; - model.Scopes = settings.Scopes != null ? string.Join(" ", settings.Scopes) : null; - model.Authority = settings.Authority?.AbsoluteUri; - model.CallbackPath = settings.CallbackPath; - model.ClientId = settings.ClientId; - model.ClientSecret = settings.ClientSecret; - model.SignedOutCallbackPath = settings.SignedOutCallbackPath; - model.SignedOutRedirectUri = settings.SignedOutRedirectUri; - model.ResponseMode = settings.ResponseMode; - model.StoreExternalTokens = settings.StoreExternalTokens; - - if (settings.ResponseType == OpenIdConnectResponseType.Code) - { - model.UseCodeFlow = true; - } - else if (settings.ResponseType == OpenIdConnectResponseType.CodeIdToken) - { - model.UseCodeIdTokenFlow = true; - } - else if (settings.ResponseType == OpenIdConnectResponseType.CodeIdTokenToken) - { - model.UseCodeIdTokenTokenFlow = true; - } - else if (settings.ResponseType == OpenIdConnectResponseType.CodeToken) - { - model.UseCodeTokenFlow = true; - } - else if (settings.ResponseType == OpenIdConnectResponseType.IdToken) - { - model.UseIdTokenFlow = true; - } - else if (settings.ResponseType == OpenIdConnectResponseType.IdTokenToken) - { - model.UseIdTokenTokenFlow = true; - } - - model.Parameters = JConvert.SerializeObject(settings.Parameters, JOptions.CamelCase); - }).Location("Content:2") - .OnGroup(SettingsGroupId); + return null; } - public override async Task UpdateAsync(ISite site, OpenIdClientSettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageClientSettings)) - { - return null; - } - - var previousClientSecret = settings.ClientSecret; - var model = new OpenIdClientSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + context.AddTenantReloadWarningWrapper(); - model.Scopes ??= string.Empty; - - settings.DisplayName = model.DisplayName; - settings.Scopes = model.Scopes.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - settings.CallbackPath = model.CallbackPath; - settings.ClientId = model.ClientId; - settings.SignedOutCallbackPath = model.SignedOutCallbackPath; - settings.SignedOutRedirectUri = model.SignedOutRedirectUri; - settings.ResponseMode = model.ResponseMode; - settings.StoreExternalTokens = model.StoreExternalTokens; - - var useClientSecret = true; - - if (model.UseCodeFlow) - { - settings.ResponseType = OpenIdConnectResponseType.Code; - } - else if (model.UseCodeIdTokenFlow) - { - settings.ResponseType = OpenIdConnectResponseType.CodeIdToken; + return Initialize("OpenIdClientSettings_Edit", model => + { + model.DisplayName = settings.DisplayName; + model.Scopes = settings.Scopes != null ? string.Join(" ", settings.Scopes) : null; + model.Authority = settings.Authority?.AbsoluteUri; + model.CallbackPath = settings.CallbackPath; + model.ClientId = settings.ClientId; + model.ClientSecret = settings.ClientSecret; + model.SignedOutCallbackPath = settings.SignedOutCallbackPath; + model.SignedOutRedirectUri = settings.SignedOutRedirectUri; + model.ResponseMode = settings.ResponseMode; + model.StoreExternalTokens = settings.StoreExternalTokens; + + if (settings.ResponseType == OpenIdConnectResponseType.Code) + { + model.UseCodeFlow = true; } - else if (model.UseCodeIdTokenTokenFlow) + else if (settings.ResponseType == OpenIdConnectResponseType.CodeIdToken) { - settings.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken; + model.UseCodeIdTokenFlow = true; } - else if (model.UseCodeTokenFlow) + else if (settings.ResponseType == OpenIdConnectResponseType.CodeIdTokenToken) { - settings.ResponseType = OpenIdConnectResponseType.CodeToken; + model.UseCodeIdTokenTokenFlow = true; } - else if (model.UseIdTokenFlow) + else if (settings.ResponseType == OpenIdConnectResponseType.CodeToken) { - settings.ResponseType = OpenIdConnectResponseType.IdToken; - useClientSecret = false; + model.UseCodeTokenFlow = true; } - else if (model.UseIdTokenTokenFlow) + else if (settings.ResponseType == OpenIdConnectResponseType.IdToken) { - settings.ResponseType = OpenIdConnectResponseType.IdTokenToken; - useClientSecret = false; + model.UseIdTokenFlow = true; } - else + else if (settings.ResponseType == OpenIdConnectResponseType.IdTokenToken) { - settings.ResponseType = OpenIdConnectResponseType.None; - useClientSecret = false; + model.UseIdTokenTokenFlow = true; } - try - { - settings.Parameters = string.IsNullOrWhiteSpace(model.Parameters) - ? [] - : JConvert.DeserializeObject(model.Parameters); - } - catch - { - context.Updater.ModelState.AddModelError(Prefix, S["The parameters are written in an incorrect format."]); - } + model.Parameters = JConvert.SerializeObject(settings.Parameters, JOptions.CamelCase); + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - if (!useClientSecret) - { - model.ClientSecret = previousClientSecret = null; - } + public override async Task UpdateAsync(ISite site, OpenIdClientSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageClientSettings)) + { + return null; + } - // Restore the client secret if the input is empty (i.e if it hasn't been reset). - if (string.IsNullOrEmpty(model.ClientSecret)) - { - settings.ClientSecret = previousClientSecret; - } - else - { - var protector = _dataProtectionProvider.CreateProtector(nameof(OpenIdClientConfiguration)); - settings.ClientSecret = protector.Protect(model.ClientSecret); - } + var previousClientSecret = settings.ClientSecret; + var model = new OpenIdClientSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - foreach (var result in await _clientService.ValidateSettingsAsync(settings)) + model.Scopes ??= string.Empty; + + settings.DisplayName = model.DisplayName; + settings.Scopes = model.Scopes.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + settings.CallbackPath = model.CallbackPath; + settings.ClientId = model.ClientId; + settings.SignedOutCallbackPath = model.SignedOutCallbackPath; + settings.SignedOutRedirectUri = model.SignedOutRedirectUri; + settings.ResponseMode = model.ResponseMode; + settings.StoreExternalTokens = model.StoreExternalTokens; + + var useClientSecret = true; + + if (model.UseCodeFlow) + { + settings.ResponseType = OpenIdConnectResponseType.Code; + } + else if (model.UseCodeIdTokenFlow) + { + settings.ResponseType = OpenIdConnectResponseType.CodeIdToken; + } + else if (model.UseCodeIdTokenTokenFlow) + { + settings.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken; + } + else if (model.UseCodeTokenFlow) + { + settings.ResponseType = OpenIdConnectResponseType.CodeToken; + } + else if (model.UseIdTokenFlow) + { + settings.ResponseType = OpenIdConnectResponseType.IdToken; + useClientSecret = false; + } + else if (model.UseIdTokenTokenFlow) + { + settings.ResponseType = OpenIdConnectResponseType.IdTokenToken; + useClientSecret = false; + } + else + { + settings.ResponseType = OpenIdConnectResponseType.None; + useClientSecret = false; + } + + try + { + settings.Parameters = string.IsNullOrWhiteSpace(model.Parameters) + ? [] + : JConvert.DeserializeObject(model.Parameters); + } + catch + { + context.Updater.ModelState.AddModelError(Prefix, S["The parameters are written in an incorrect format."]); + } + + if (!useClientSecret) + { + model.ClientSecret = previousClientSecret = null; + } + + // Restore the client secret if the input is empty (i.e if it hasn't been reset). + if (string.IsNullOrEmpty(model.ClientSecret)) + { + settings.ClientSecret = previousClientSecret; + } + else + { + var protector = _dataProtectionProvider.CreateProtector(nameof(OpenIdClientConfiguration)); + settings.ClientSecret = protector.Protect(model.ClientSecret); + } + + foreach (var result in await _clientService.ValidateSettingsAsync(settings)) + { + if (result != ValidationResult.Success) { - if (result != ValidationResult.Success) - { - var key = result.MemberNames.FirstOrDefault() ?? string.Empty; - context.Updater.ModelState.AddModelError(key, result.ErrorMessage); - } + var key = result.MemberNames.FirstOrDefault() ?? string.Empty; + context.Updater.ModelState.AddModelError(key, result.ErrorMessage); } + } - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdServerSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdServerSettingsDisplayDriver.cs index 9ff33b459ad..c010c85f8e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdServerSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdServerSettingsDisplayDriver.cs @@ -8,112 +8,111 @@ using OrchardCore.OpenId.ViewModels; using static OrchardCore.OpenId.ViewModels.OpenIdServerSettingsViewModel; -namespace OrchardCore.OpenId.Drivers +namespace OrchardCore.OpenId.Drivers; + +public sealed class OpenIdServerSettingsDisplayDriver : DisplayDriver { - public sealed class OpenIdServerSettingsDisplayDriver : DisplayDriver - { - private readonly IOpenIdServerService _serverService; + private readonly IOpenIdServerService _serverService; - public OpenIdServerSettingsDisplayDriver(IOpenIdServerService serverService) - => _serverService = serverService; + public OpenIdServerSettingsDisplayDriver(IOpenIdServerService serverService) + => _serverService = serverService; - public override IDisplayResult Edit(OpenIdServerSettings settings, BuildEditorContext context) - { - context.AddTenantReloadWarningWrapper(); + public override IDisplayResult Edit(OpenIdServerSettings settings, BuildEditorContext context) + { + context.AddTenantReloadWarningWrapper(); - return Initialize("OpenIdServerSettings_Edit", async model => + return Initialize("OpenIdServerSettings_Edit", async model => + { + model.AccessTokenFormat = settings.AccessTokenFormat; + model.Authority = settings.Authority?.AbsoluteUri; + + model.EncryptionCertificateStoreLocation = settings.EncryptionCertificateStoreLocation; + model.EncryptionCertificateStoreName = settings.EncryptionCertificateStoreName; + model.EncryptionCertificateThumbprint = settings.EncryptionCertificateThumbprint; + + model.SigningCertificateStoreLocation = settings.SigningCertificateStoreLocation; + model.SigningCertificateStoreName = settings.SigningCertificateStoreName; + model.SigningCertificateThumbprint = settings.SigningCertificateThumbprint; + + model.EnableAuthorizationEndpoint = settings.AuthorizationEndpointPath.HasValue; + model.EnableLogoutEndpoint = settings.LogoutEndpointPath.HasValue; + model.EnableTokenEndpoint = settings.TokenEndpointPath.HasValue; + model.EnableUserInfoEndpoint = settings.UserinfoEndpointPath.HasValue; + model.EnableIntrospectionEndpoint = settings.IntrospectionEndpointPath.HasValue; + model.EnableRevocationEndpoint = settings.RevocationEndpointPath.HasValue; + + model.AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow; + model.AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow; + model.AllowHybridFlow = settings.AllowHybridFlow; + model.AllowImplicitFlow = settings.AllowImplicitFlow; + model.AllowPasswordFlow = settings.AllowPasswordFlow; + model.AllowRefreshTokenFlow = settings.AllowRefreshTokenFlow; + + model.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption; + model.DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens; + model.UseReferenceAccessTokens = settings.UseReferenceAccessTokens; + model.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange; + + foreach (var (certificate, location, name) in await _serverService.GetAvailableCertificatesAsync()) { - model.AccessTokenFormat = settings.AccessTokenFormat; - model.Authority = settings.Authority?.AbsoluteUri; - - model.EncryptionCertificateStoreLocation = settings.EncryptionCertificateStoreLocation; - model.EncryptionCertificateStoreName = settings.EncryptionCertificateStoreName; - model.EncryptionCertificateThumbprint = settings.EncryptionCertificateThumbprint; - - model.SigningCertificateStoreLocation = settings.SigningCertificateStoreLocation; - model.SigningCertificateStoreName = settings.SigningCertificateStoreName; - model.SigningCertificateThumbprint = settings.SigningCertificateThumbprint; - - model.EnableAuthorizationEndpoint = settings.AuthorizationEndpointPath.HasValue; - model.EnableLogoutEndpoint = settings.LogoutEndpointPath.HasValue; - model.EnableTokenEndpoint = settings.TokenEndpointPath.HasValue; - model.EnableUserInfoEndpoint = settings.UserinfoEndpointPath.HasValue; - model.EnableIntrospectionEndpoint = settings.IntrospectionEndpointPath.HasValue; - model.EnableRevocationEndpoint = settings.RevocationEndpointPath.HasValue; - - model.AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow; - model.AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow; - model.AllowHybridFlow = settings.AllowHybridFlow; - model.AllowImplicitFlow = settings.AllowImplicitFlow; - model.AllowPasswordFlow = settings.AllowPasswordFlow; - model.AllowRefreshTokenFlow = settings.AllowRefreshTokenFlow; - - model.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption; - model.DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens; - model.UseReferenceAccessTokens = settings.UseReferenceAccessTokens; - model.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange; - - foreach (var (certificate, location, name) in await _serverService.GetAvailableCertificatesAsync()) + model.AvailableCertificates.Add(new CertificateInfo { - model.AvailableCertificates.Add(new CertificateInfo - { - StoreLocation = location, - StoreName = name, - FriendlyName = certificate.FriendlyName, - Issuer = certificate.Issuer, - Subject = certificate.Subject, - NotBefore = certificate.NotBefore, - NotAfter = certificate.NotAfter, - ThumbPrint = certificate.Thumbprint, - HasPrivateKey = certificate.HasPrivateKey, - Archived = certificate.Archived - }); - } - }).Location("Content:2"); - } - - public override async Task UpdateAsync(OpenIdServerSettings settings, UpdateEditorContext context) - { - var model = new OpenIdServerSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - settings.AccessTokenFormat = model.AccessTokenFormat; - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - - settings.EncryptionCertificateStoreLocation = model.EncryptionCertificateStoreLocation; - settings.EncryptionCertificateStoreName = model.EncryptionCertificateStoreName; - settings.EncryptionCertificateThumbprint = model.EncryptionCertificateThumbprint; - - settings.SigningCertificateStoreLocation = model.SigningCertificateStoreLocation; - settings.SigningCertificateStoreName = model.SigningCertificateStoreName; - settings.SigningCertificateThumbprint = model.SigningCertificateThumbprint; - - settings.AuthorizationEndpointPath = model.EnableAuthorizationEndpoint ? - new PathString("/connect/authorize") : PathString.Empty; - settings.LogoutEndpointPath = model.EnableLogoutEndpoint ? - new PathString("/connect/logout") : PathString.Empty; - settings.TokenEndpointPath = model.EnableTokenEndpoint ? - new PathString("/connect/token") : PathString.Empty; - settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ? - new PathString("/connect/userinfo") : PathString.Empty; - settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ? - new PathString("/connect/introspect") : PathString.Empty; - settings.RevocationEndpointPath = model.EnableRevocationEndpoint ? - new PathString("/connect/revoke") : PathString.Empty; - - settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow; - settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow; - settings.AllowHybridFlow = model.AllowHybridFlow; - settings.AllowImplicitFlow = model.AllowImplicitFlow; - settings.AllowPasswordFlow = model.AllowPasswordFlow; - settings.AllowRefreshTokenFlow = model.AllowRefreshTokenFlow; - - settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption; - settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens; - settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens; - settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange; - - return await EditAsync(settings, context); - } + StoreLocation = location, + StoreName = name, + FriendlyName = certificate.FriendlyName, + Issuer = certificate.Issuer, + Subject = certificate.Subject, + NotBefore = certificate.NotBefore, + NotAfter = certificate.NotAfter, + ThumbPrint = certificate.Thumbprint, + HasPrivateKey = certificate.HasPrivateKey, + Archived = certificate.Archived + }); + } + }).Location("Content:2"); + } + + public override async Task UpdateAsync(OpenIdServerSettings settings, UpdateEditorContext context) + { + var model = new OpenIdServerSettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + + settings.AccessTokenFormat = model.AccessTokenFormat; + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + + settings.EncryptionCertificateStoreLocation = model.EncryptionCertificateStoreLocation; + settings.EncryptionCertificateStoreName = model.EncryptionCertificateStoreName; + settings.EncryptionCertificateThumbprint = model.EncryptionCertificateThumbprint; + + settings.SigningCertificateStoreLocation = model.SigningCertificateStoreLocation; + settings.SigningCertificateStoreName = model.SigningCertificateStoreName; + settings.SigningCertificateThumbprint = model.SigningCertificateThumbprint; + + settings.AuthorizationEndpointPath = model.EnableAuthorizationEndpoint ? + new PathString("/connect/authorize") : PathString.Empty; + settings.LogoutEndpointPath = model.EnableLogoutEndpoint ? + new PathString("/connect/logout") : PathString.Empty; + settings.TokenEndpointPath = model.EnableTokenEndpoint ? + new PathString("/connect/token") : PathString.Empty; + settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ? + new PathString("/connect/userinfo") : PathString.Empty; + settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ? + new PathString("/connect/introspect") : PathString.Empty; + settings.RevocationEndpointPath = model.EnableRevocationEndpoint ? + new PathString("/connect/revoke") : PathString.Empty; + + settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow; + settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow; + settings.AllowHybridFlow = model.AllowHybridFlow; + settings.AllowImplicitFlow = model.AllowImplicitFlow; + settings.AllowPasswordFlow = model.AllowPasswordFlow; + settings.AllowRefreshTokenFlow = model.AllowRefreshTokenFlow; + + settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption; + settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens; + settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens; + settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange; + + return await EditAsync(settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdValidationSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdValidationSettingsDisplayDriver.cs index b0ae23aa395..58aaff01b87 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdValidationSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Drivers/OpenIdValidationSettingsDisplayDriver.cs @@ -10,61 +10,60 @@ using OrchardCore.OpenId.Settings; using OrchardCore.OpenId.ViewModels; -namespace OrchardCore.OpenId.Drivers +namespace OrchardCore.OpenId.Drivers; + +public sealed class OpenIdValidationSettingsDisplayDriver : DisplayDriver { - public sealed class OpenIdValidationSettingsDisplayDriver : DisplayDriver - { - private readonly IShellHost _shellHost; + private readonly IShellHost _shellHost; - public OpenIdValidationSettingsDisplayDriver(IShellHost shellHost) - => _shellHost = shellHost; + public OpenIdValidationSettingsDisplayDriver(IShellHost shellHost) + => _shellHost = shellHost; - public override IDisplayResult Edit(OpenIdValidationSettings settings, BuildEditorContext context) + public override IDisplayResult Edit(OpenIdValidationSettings settings, BuildEditorContext context) + { + context.AddTenantReloadWarningWrapper(); + + return Initialize("OpenIdValidationSettings_Edit", async model => { - context.AddTenantReloadWarningWrapper(); + model.Authority = settings.Authority?.AbsoluteUri; + model.MetadataAddress = settings.MetadataAddress?.AbsoluteUri; + model.Audience = settings.Audience; + model.DisableTokenTypeValidation = settings.DisableTokenTypeValidation; + model.Tenant = settings.Tenant; - return Initialize("OpenIdValidationSettings_Edit", async model => - { - model.Authority = settings.Authority?.AbsoluteUri; - model.MetadataAddress = settings.MetadataAddress?.AbsoluteUri; - model.Audience = settings.Audience; - model.DisableTokenTypeValidation = settings.DisableTokenTypeValidation; - model.Tenant = settings.Tenant; + var availableTenants = new List(); - var availableTenants = new List(); + foreach (var shellSettings in _shellHost.GetAllSettings().Where(s => s.IsRunning())) + { + var shellScope = await _shellHost.GetScopeAsync(shellSettings); - foreach (var shellSettings in _shellHost.GetAllSettings().Where(s => s.IsRunning())) + await shellScope.UsingAsync(scope => { - var shellScope = await _shellHost.GetScopeAsync(shellSettings); - - await shellScope.UsingAsync(scope => + var descriptor = scope.ServiceProvider.GetRequiredService(); + if (descriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) { - var descriptor = scope.ServiceProvider.GetRequiredService(); - if (descriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) - { - availableTenants.Add(shellSettings.Name); - } - return Task.CompletedTask; - }); - } + availableTenants.Add(shellSettings.Name); + } + return Task.CompletedTask; + }); + } - model.AvailableTenants = availableTenants; - }).Location("Content:2"); - } + model.AvailableTenants = availableTenants; + }).Location("Content:2"); + } - public override async Task UpdateAsync(OpenIdValidationSettings settings, UpdateEditorContext context) - { - var model = new OpenIdValidationSettingsViewModel(); + public override async Task UpdateAsync(OpenIdValidationSettings settings, UpdateEditorContext context) + { + var model = new OpenIdValidationSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - settings.MetadataAddress = !string.IsNullOrEmpty(model.MetadataAddress) ? new Uri(model.MetadataAddress, UriKind.Absolute) : null; - settings.Audience = model.Audience?.Trim(); - settings.DisableTokenTypeValidation = model.DisableTokenTypeValidation; - settings.Tenant = model.Tenant; + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + settings.MetadataAddress = !string.IsNullOrEmpty(model.MetadataAddress) ? new Uri(model.MetadataAddress, UriKind.Absolute) : null; + settings.Audience = model.Audience?.Trim(); + settings.DisableTokenTypeValidation = model.DisableTokenTypeValidation; + settings.Tenant = model.Tenant; - return await EditAsync(settings, context); - } + return await EditAsync(settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Handlers/OpenIdApplicationRoleRemovedEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Handlers/OpenIdApplicationRoleRemovedEventHandler.cs index cbfbca53565..764fc36017f 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Handlers/OpenIdApplicationRoleRemovedEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Handlers/OpenIdApplicationRoleRemovedEventHandler.cs @@ -3,22 +3,21 @@ using OrchardCore.OpenId.Abstractions.Managers; using OrchardCore.Security; -namespace OrchardCore.OpenId.Services.Handlers +namespace OrchardCore.OpenId.Services.Handlers; + +public class OpenIdApplicationRoleRemovedEventHandler : IRoleRemovedEventHandler { - public class OpenIdApplicationRoleRemovedEventHandler : IRoleRemovedEventHandler - { - private readonly IOpenIdApplicationManager _manager; + private readonly IOpenIdApplicationManager _manager; - public OpenIdApplicationRoleRemovedEventHandler(IOpenIdApplicationManager manager) - => _manager = manager; + public OpenIdApplicationRoleRemovedEventHandler(IOpenIdApplicationManager manager) + => _manager = manager; - public async Task RoleRemovedAsync(string roleName) + public async Task RoleRemovedAsync(string roleName) + { + await foreach (var application in _manager.ListInRoleAsync(roleName)) { - await foreach (var application in _manager.ListInRoleAsync(roleName)) - { - var roles = await _manager.GetRolesAsync(application); - await _manager.SetRolesAsync(application, roles.Remove(roleName, StringComparer.OrdinalIgnoreCase)); - } + var roles = await _manager.GetRolesAsync(application); + await _manager.SetRolesAsync(application, roles.Remove(roleName, StringComparer.OrdinalIgnoreCase)); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdApplicationSettings.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdApplicationSettings.cs index 7c634610ada..1f0682a5968 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdApplicationSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdApplicationSettings.cs @@ -5,250 +5,249 @@ using OrchardCore.OpenId.Abstractions.Descriptors; using OrchardCore.OpenId.Abstractions.Managers; -namespace OrchardCore.OpenId +namespace OrchardCore.OpenId; + +public class OpenIdApplicationSettings { - public class OpenIdApplicationSettings - { - public string ClientId { get; set; } - public string DisplayName { get; set; } - public string RedirectUris { get; set; } - public string PostLogoutRedirectUris { get; set; } - public string Type { get; set; } - public string ConsentType { get; set; } - public string ClientSecret { get; set; } - public string[] Roles { get; set; } - public string[] Scopes { get; set; } - public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } - public bool AllowLogoutEndpoint { get; set; } - public bool AllowIntrospectionEndpoint { get; set; } - public bool AllowRevocationEndpoint { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } - } + public string ClientId { get; set; } + public string DisplayName { get; set; } + public string RedirectUris { get; set; } + public string PostLogoutRedirectUris { get; set; } + public string Type { get; set; } + public string ConsentType { get; set; } + public string ClientSecret { get; set; } + public string[] Roles { get; set; } + public string[] Scopes { get; set; } + public bool AllowPasswordFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } + public bool AllowHybridFlow { get; set; } + public bool AllowImplicitFlow { get; set; } + public bool AllowLogoutEndpoint { get; set; } + public bool AllowIntrospectionEndpoint { get; set; } + public bool AllowRevocationEndpoint { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } +} + +internal static class OpenIdApplicationExtensions +{ + internal static readonly string[] _separator = [" ", ","]; - internal static class OpenIdApplicationExtensions + public static async Task UpdateDescriptorFromSettings(this IOpenIdApplicationManager _applicationManager, OpenIdApplicationSettings model, object application = null) { - internal static readonly string[] _separator = [" ", ","]; + var descriptor = new OpenIdApplicationDescriptor(); - public static async Task UpdateDescriptorFromSettings(this IOpenIdApplicationManager _applicationManager, OpenIdApplicationSettings model, object application = null) + if (application != null) { - var descriptor = new OpenIdApplicationDescriptor(); + await _applicationManager.PopulateAsync(descriptor, application); + } - if (application != null) - { - await _applicationManager.PopulateAsync(descriptor, application); - } + descriptor.ClientId = model.ClientId; + descriptor.ConsentType = model.ConsentType; + descriptor.DisplayName = model.DisplayName; + descriptor.ClientType = model.Type; - descriptor.ClientId = model.ClientId; - descriptor.ConsentType = model.ConsentType; - descriptor.DisplayName = model.DisplayName; - descriptor.ClientType = model.Type; + if (!string.IsNullOrEmpty(model.ClientSecret)) + { + descriptor.ClientSecret = model.ClientSecret; + } - if (!string.IsNullOrEmpty(model.ClientSecret)) - { - descriptor.ClientSecret = model.ClientSecret; - } + if (string.Equals(descriptor.ClientType, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) + { + descriptor.ClientSecret = null; + } - if (string.Equals(descriptor.ClientType, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) - { - descriptor.ClientSecret = null; - } + if (model.AllowLogoutEndpoint) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Logout); + } - if (model.AllowLogoutEndpoint) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Logout); - } + if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); + } - if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode); - } + if (model.AllowClientCredentialsFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); + } - if (model.AllowClientCredentialsFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials); - } + if (model.AllowHybridFlow || model.AllowImplicitFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.Implicit); + } - if (model.AllowHybridFlow || model.AllowImplicitFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.Implicit); - } + if (model.AllowPasswordFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.Password); + } - if (model.AllowPasswordFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.Password); - } + if (model.AllowRefreshTokenFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); + } - if (model.AllowRefreshTokenFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.GrantTypes.RefreshToken); - } + if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow || model.AllowImplicitFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Authorization); + } - if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow || model.AllowImplicitFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Authorization); - } + if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow || + model.AllowClientCredentialsFlow || model.AllowPasswordFlow || model.AllowRefreshTokenFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Token); + } - if (model.AllowAuthorizationCodeFlow || model.AllowHybridFlow || - model.AllowClientCredentialsFlow || model.AllowPasswordFlow || model.AllowRefreshTokenFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Token); - } + if (model.AllowAuthorizationCodeFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Code); + } - if (model.AllowAuthorizationCodeFlow) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Code); - } + if (model.AllowImplicitFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken); - if (model.AllowImplicitFlow) + if (string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken); - - if (string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Token); - } + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token); } else { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdToken); descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Token); } + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdToken); + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken); + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Token); + } - if (model.AllowHybridFlow) + if (model.AllowHybridFlow) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); + + if (string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); - - if (string.Equals(model.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); - descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); - } + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); + descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); } else { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); } + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken); + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken); + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeToken); + } - if (model.AllowIntrospectionEndpoint) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Introspection); - } - - if (model.AllowRevocationEndpoint) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); - } - else - { - descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Revocation); - } + if (model.AllowIntrospectionEndpoint) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Introspection); + } - if (model.RequireProofKeyForCodeExchange) - { - descriptor.Requirements.Add(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange); - } - else - { - descriptor.Requirements.Remove(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange); - } + if (model.AllowRevocationEndpoint) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation); + } + else + { + descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Revocation); + } - descriptor.Roles.Clear(); + if (model.RequireProofKeyForCodeExchange) + { + descriptor.Requirements.Add(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange); + } + else + { + descriptor.Requirements.Remove(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange); + } - foreach (var role in model.Roles) - { - descriptor.Roles.Add(role); - } + descriptor.Roles.Clear(); - descriptor.Permissions.RemoveWhere(permission => permission.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope)); - foreach (var scope in model.Scopes) - { - descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope); - } + foreach (var role in model.Roles) + { + descriptor.Roles.Add(role); + } - descriptor.PostLogoutRedirectUris.Clear(); - foreach (var uri in - (from uri in model.PostLogoutRedirectUris?.Split(_separator, StringSplitOptions.RemoveEmptyEntries) ?? [] - select new Uri(uri, UriKind.Absolute))) - { - descriptor.PostLogoutRedirectUris.Add(uri); - } + descriptor.Permissions.RemoveWhere(permission => permission.StartsWith(OpenIddictConstants.Permissions.Prefixes.Scope)); + foreach (var scope in model.Scopes) + { + descriptor.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope); + } - descriptor.RedirectUris.Clear(); - foreach (var uri in - (from uri in model.RedirectUris?.Split(_separator, StringSplitOptions.RemoveEmptyEntries) ?? [] - select new Uri(uri, UriKind.Absolute))) - { - descriptor.RedirectUris.Add(uri); - } + descriptor.PostLogoutRedirectUris.Clear(); + foreach (var uri in + (from uri in model.PostLogoutRedirectUris?.Split(_separator, StringSplitOptions.RemoveEmptyEntries) ?? [] + select new Uri(uri, UriKind.Absolute))) + { + descriptor.PostLogoutRedirectUris.Add(uri); + } - if (application == null) - { - await _applicationManager.CreateAsync(descriptor); - } - else - { - await _applicationManager.UpdateAsync(application, descriptor); - } + descriptor.RedirectUris.Clear(); + foreach (var uri in + (from uri in model.RedirectUris?.Split(_separator, StringSplitOptions.RemoveEmptyEntries) ?? [] + select new Uri(uri, UriKind.Absolute))) + { + descriptor.RedirectUris.Add(uri); + } + if (application == null) + { + await _applicationManager.CreateAsync(descriptor); } + else + { + await _applicationManager.UpdateAsync(application, descriptor); + } + } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdExtensions.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdExtensions.cs index c5b808e913f..81ab9f7f261 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdExtensions.cs @@ -4,52 +4,51 @@ using System.Threading.Tasks; using static OpenIddict.Abstractions.OpenIddictConstants; -namespace OrchardCore.OpenId +namespace OrchardCore.OpenId; + +internal static class OpenIdExtensions { - internal static class OpenIdExtensions + internal static string GetUserIdentifier(this ClaimsIdentity identity) + => identity.FindFirst(Claims.Subject)?.Value ?? + identity.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? + identity.FindFirst(ClaimTypes.Upn)?.Value ?? + throw new InvalidOperationException("No suitable user identifier can be found in the identity."); + + internal static string GetUserIdentifier(this ClaimsPrincipal principal) + => principal.FindFirst(Claims.Subject)?.Value ?? + principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? + principal.FindFirst(ClaimTypes.Upn)?.Value ?? + throw new InvalidOperationException("No suitable user identifier can be found in the principal."); + + internal static string GetUserName(this ClaimsPrincipal principal) + => principal.FindFirst(Claims.Name)?.Value ?? + principal.FindFirst(ClaimTypes.Name)?.Value ?? + throw new InvalidOperationException("No suitable user name can be found in the principal."); + + internal static async Task AnyAsync(this IAsyncEnumerable source) { - internal static string GetUserIdentifier(this ClaimsIdentity identity) - => identity.FindFirst(Claims.Subject)?.Value ?? - identity.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? - identity.FindFirst(ClaimTypes.Upn)?.Value ?? - throw new InvalidOperationException("No suitable user identifier can be found in the identity."); - - internal static string GetUserIdentifier(this ClaimsPrincipal principal) - => principal.FindFirst(Claims.Subject)?.Value ?? - principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? - principal.FindFirst(ClaimTypes.Upn)?.Value ?? - throw new InvalidOperationException("No suitable user identifier can be found in the principal."); - - internal static string GetUserName(this ClaimsPrincipal principal) - => principal.FindFirst(Claims.Name)?.Value ?? - principal.FindFirst(ClaimTypes.Name)?.Value ?? - throw new InvalidOperationException("No suitable user name can be found in the principal."); - - internal static async Task AnyAsync(this IAsyncEnumerable source) - { - ArgumentNullException.ThrowIfNull(source); - - await using var enumerator = source.GetAsyncEnumerator(); - return await enumerator.MoveNextAsync(); - } + ArgumentNullException.ThrowIfNull(source); - internal static Task> ToListAsync(this IAsyncEnumerable source) - { - ArgumentNullException.ThrowIfNull(source); + await using var enumerator = source.GetAsyncEnumerator(); + return await enumerator.MoveNextAsync(); + } - return ExecuteAsync(); + internal static Task> ToListAsync(this IAsyncEnumerable source) + { + ArgumentNullException.ThrowIfNull(source); - async Task> ExecuteAsync() - { - var list = new List(); + return ExecuteAsync(); - await foreach (var element in source) - { - list.Add(element); - } + async Task> ExecuteAsync() + { + var list = new List(); - return list; + await foreach (var element in source) + { + list.Add(element); } + + return list; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStep.cs index ccc2bae111f..6f7de446179 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStep.cs @@ -6,54 +6,53 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public sealed class OpenIdApplicationStep : IRecipeStepHandler { - public sealed class OpenIdApplicationStep : IRecipeStepHandler + private readonly IOpenIdApplicationManager _applicationManager; + + /// + /// This recipe step adds an OpenID Connect app. + /// + public OpenIdApplicationStep(IOpenIdApplicationManager applicationManager) { - private readonly IOpenIdApplicationManager _applicationManager; + _applicationManager = applicationManager; + } - /// - /// This recipe step adds an OpenID Connect app. - /// - public OpenIdApplicationStep(IOpenIdApplicationManager applicationManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "OpenIdApplication", StringComparison.OrdinalIgnoreCase)) { - _applicationManager = applicationManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "OpenIdApplication", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var app = await _applicationManager.FindByClientIdAsync(model.ClientId); + var model = context.Step.ToObject(); + var app = await _applicationManager.FindByClientIdAsync(model.ClientId); - var settings = new OpenIdApplicationSettings() - { - AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, - AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, - AllowHybridFlow = model.AllowHybridFlow, - AllowImplicitFlow = model.AllowImplicitFlow, - AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, - AllowLogoutEndpoint = model.AllowLogoutEndpoint, - AllowPasswordFlow = model.AllowPasswordFlow, - AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, - AllowRevocationEndpoint = model.AllowRevocationEndpoint, - ClientId = model.ClientId, - ClientSecret = model.ClientSecret, - ConsentType = model.ConsentType, - DisplayName = model.DisplayName, - PostLogoutRedirectUris = model.PostLogoutRedirectUris, - RedirectUris = model.RedirectUris, - Roles = model.RoleEntries.Select(x => x.Name).ToArray(), - Scopes = model.ScopeEntries.Select(x => x.Name).ToArray(), - Type = model.Type, - RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange, - }; + var settings = new OpenIdApplicationSettings() + { + AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow, + AllowClientCredentialsFlow = model.AllowClientCredentialsFlow, + AllowHybridFlow = model.AllowHybridFlow, + AllowImplicitFlow = model.AllowImplicitFlow, + AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint, + AllowLogoutEndpoint = model.AllowLogoutEndpoint, + AllowPasswordFlow = model.AllowPasswordFlow, + AllowRefreshTokenFlow = model.AllowRefreshTokenFlow, + AllowRevocationEndpoint = model.AllowRevocationEndpoint, + ClientId = model.ClientId, + ClientSecret = model.ClientSecret, + ConsentType = model.ConsentType, + DisplayName = model.DisplayName, + PostLogoutRedirectUris = model.PostLogoutRedirectUris, + RedirectUris = model.RedirectUris, + Roles = model.RoleEntries.Select(x => x.Name).ToArray(), + Scopes = model.ScopeEntries.Select(x => x.Name).ToArray(), + Type = model.Type, + RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange, + }; - await _applicationManager.UpdateDescriptorFromSettings(settings, app); - } + await _applicationManager.UpdateDescriptorFromSettings(settings, app); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStepModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStepModel.cs index 3015c25a778..a41023d416b 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStepModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdApplicationStepModel.cs @@ -1,35 +1,34 @@ -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public class OpenIdApplicationStepModel { - public class OpenIdApplicationStepModel - { - public string ClientId { get; set; } - public string DisplayName { get; set; } - public string RedirectUris { get; set; } - public string PostLogoutRedirectUris { get; set; } - public string Type { get; set; } - public string ConsentType { get; set; } - public string ClientSecret { get; set; } - public RoleEntry[] RoleEntries { get; set; } = []; - public ScopeEntry[] ScopeEntries { get; set; } = []; - public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } - public bool AllowLogoutEndpoint { get; set; } - public bool AllowIntrospectionEndpoint { get; set; } - public bool AllowRevocationEndpoint { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + public string ClientId { get; set; } + public string DisplayName { get; set; } + public string RedirectUris { get; set; } + public string PostLogoutRedirectUris { get; set; } + public string Type { get; set; } + public string ConsentType { get; set; } + public string ClientSecret { get; set; } + public RoleEntry[] RoleEntries { get; set; } = []; + public ScopeEntry[] ScopeEntries { get; set; } = []; + public bool AllowPasswordFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } + public bool AllowHybridFlow { get; set; } + public bool AllowImplicitFlow { get; set; } + public bool AllowLogoutEndpoint { get; set; } + public bool AllowIntrospectionEndpoint { get; set; } + public bool AllowRevocationEndpoint { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public class RoleEntry - { - public string Name { get; set; } - } + public class RoleEntry + { + public string Name { get; set; } + } - public class ScopeEntry - { - public string Name { get; set; } - } + public class ScopeEntry + { + public string Name { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdClientSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdClientSettingsStep.cs index 6a1a0d19ee9..c4cb37f2ab7 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdClientSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdClientSettingsStep.cs @@ -6,63 +6,62 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +/// +/// This recipe step sets general OpenID Connect Client settings. +/// +public sealed class OpenIdClientSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets general OpenID Connect Client settings. - /// - public sealed class OpenIdClientSettingsStep : IRecipeStepHandler + private readonly IOpenIdClientService _clientService; + + public OpenIdClientSettingsStep(IOpenIdClientService clientService) { - private readonly IOpenIdClientService _clientService; + _clientService = clientService; + } - public OpenIdClientSettingsStep(IOpenIdClientService clientService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "OpenIdClientSettings", StringComparison.OrdinalIgnoreCase)) { - _clientService = clientService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "OpenIdClientSettings", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _clientService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _clientService.LoadSettingsAsync(); - settings.Scopes = model.Scopes.Split(' ', ','); - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - settings.CallbackPath = model.CallbackPath; - settings.ClientId = model.ClientId; - settings.ClientSecret = model.ClientSecret; - settings.DisplayName = model.DisplayName; - settings.ResponseMode = model.ResponseMode; - settings.ResponseType = model.ResponseType; - settings.SignedOutCallbackPath = model.SignedOutCallbackPath; - settings.SignedOutRedirectUri = model.SignedOutRedirectUri; - settings.StoreExternalTokens = model.StoreExternalTokens; - settings.Parameters = model.Parameters; + settings.Scopes = model.Scopes.Split(' ', ','); + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + settings.CallbackPath = model.CallbackPath; + settings.ClientId = model.ClientId; + settings.ClientSecret = model.ClientSecret; + settings.DisplayName = model.DisplayName; + settings.ResponseMode = model.ResponseMode; + settings.ResponseType = model.ResponseType; + settings.SignedOutCallbackPath = model.SignedOutCallbackPath; + settings.SignedOutRedirectUri = model.SignedOutRedirectUri; + settings.StoreExternalTokens = model.StoreExternalTokens; + settings.Parameters = model.Parameters; - await _clientService.UpdateSettingsAsync(settings); - } + await _clientService.UpdateSettingsAsync(settings); } +} - public sealed class OpenIdClientSettingsStepModel - { - public string DisplayName { get; set; } +public sealed class OpenIdClientSettingsStepModel +{ + public string DisplayName { get; set; } - [Url] - public string Authority { get; set; } + [Url] + public string Authority { get; set; } - public string ClientId { get; set; } - public string ClientSecret { get; set; } - public string CallbackPath { get; set; } - public string SignedOutRedirectUri { get; set; } - public string SignedOutCallbackPath { get; set; } - public string Scopes { get; set; } - public string ResponseType { get; set; } - public string ResponseMode { get; set; } - public bool StoreExternalTokens { get; set; } - public ParameterSetting[] Parameters { get; set; } - } + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public string CallbackPath { get; set; } + public string SignedOutRedirectUri { get; set; } + public string SignedOutCallbackPath { get; set; } + public string Scopes { get; set; } + public string ResponseType { get; set; } + public string ResponseMode { get; set; } + public bool StoreExternalTokens { get; set; } + public ParameterSetting[] Parameters { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStep.cs index 4402739198c..e255efa8ed9 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStep.cs @@ -6,57 +6,56 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public sealed class OpenIdScopeStep : IRecipeStepHandler { - public sealed class OpenIdScopeStep : IRecipeStepHandler + private readonly IOpenIdScopeManager _scopeManager; + + /// + /// This recipe step adds an OpenID Connect scope. + /// + public OpenIdScopeStep(IOpenIdScopeManager scopeManager) { - private readonly IOpenIdScopeManager _scopeManager; + _scopeManager = scopeManager; + } + + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "OpenIdScope", StringComparison.OrdinalIgnoreCase)) + { + return; + } - /// - /// This recipe step adds an OpenID Connect scope. - /// - public OpenIdScopeStep(IOpenIdScopeManager scopeManager) + var model = context.Step.ToObject(); + var scope = await _scopeManager.FindByNameAsync(model.ScopeName); + var descriptor = new OpenIdScopeDescriptor(); + var isNew = true; + + if (scope != null) { - _scopeManager = scopeManager; + isNew = false; + await _scopeManager.PopulateAsync(scope, descriptor); } - public async Task ExecuteAsync(RecipeExecutionContext context) + descriptor.Description = model.Description; + descriptor.Name = model.ScopeName; + descriptor.DisplayName = model.DisplayName; + + if (!string.IsNullOrEmpty(model.Resources)) + { + descriptor.Resources.Clear(); + descriptor.Resources.UnionWith( + model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); + } + + if (isNew) + { + await _scopeManager.CreateAsync(descriptor); + } + else { - if (!string.Equals(context.Name, "OpenIdScope", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var scope = await _scopeManager.FindByNameAsync(model.ScopeName); - var descriptor = new OpenIdScopeDescriptor(); - var isNew = true; - - if (scope != null) - { - isNew = false; - await _scopeManager.PopulateAsync(scope, descriptor); - } - - descriptor.Description = model.Description; - descriptor.Name = model.ScopeName; - descriptor.DisplayName = model.DisplayName; - - if (!string.IsNullOrEmpty(model.Resources)) - { - descriptor.Resources.Clear(); - descriptor.Resources.UnionWith( - model.Resources.Split(' ', StringSplitOptions.RemoveEmptyEntries)); - } - - if (isNew) - { - await _scopeManager.CreateAsync(descriptor); - } - else - { - await _scopeManager.UpdateAsync(scope, descriptor); - } + await _scopeManager.UpdateAsync(scope, descriptor); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStepModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStepModel.cs index 6b65d539c47..2ca78ef3198 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStepModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdScopeStepModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public class OpenIdScopeStepModel { - public class OpenIdScopeStepModel - { - public string Description { get; set; } - public string DisplayName { get; set; } - public string ScopeName { get; set; } - public string Resources { get; set; } - } + public string Description { get; set; } + public string DisplayName { get; set; } + public string ScopeName { get; set; } + public string Resources { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStep.cs index f79794e085a..3f82540b00d 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStep.cs @@ -7,67 +7,66 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +/// +/// This recipe step sets general OpenID Connect settings. +/// +public sealed class OpenIdServerSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets general OpenID Connect settings. - /// - public sealed class OpenIdServerSettingsStep : IRecipeStepHandler + private readonly IOpenIdServerService _serverService; + + public OpenIdServerSettingsStep(IOpenIdServerService serverService) { - private readonly IOpenIdServerService _serverService; + _serverService = serverService; + } - public OpenIdServerSettingsStep(IOpenIdServerService serverService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(OpenIdServerSettings), StringComparison.OrdinalIgnoreCase)) { - _serverService = serverService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(OpenIdServerSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _serverService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _serverService.LoadSettingsAsync(); - settings.AccessTokenFormat = model.AccessTokenFormat; - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + settings.AccessTokenFormat = model.AccessTokenFormat; + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - settings.EncryptionCertificateStoreLocation = model.EncryptionCertificateStoreLocation; - settings.EncryptionCertificateStoreName = model.EncryptionCertificateStoreName; - settings.EncryptionCertificateThumbprint = model.EncryptionCertificateThumbprint; + settings.EncryptionCertificateStoreLocation = model.EncryptionCertificateStoreLocation; + settings.EncryptionCertificateStoreName = model.EncryptionCertificateStoreName; + settings.EncryptionCertificateThumbprint = model.EncryptionCertificateThumbprint; - settings.SigningCertificateStoreLocation = model.SigningCertificateStoreLocation; - settings.SigningCertificateStoreName = model.SigningCertificateStoreName; - settings.SigningCertificateThumbprint = model.SigningCertificateThumbprint; + settings.SigningCertificateStoreLocation = model.SigningCertificateStoreLocation; + settings.SigningCertificateStoreName = model.SigningCertificateStoreName; + settings.SigningCertificateThumbprint = model.SigningCertificateThumbprint; - settings.AuthorizationEndpointPath = model.EnableAuthorizationEndpoint ? - new PathString("/connect/authorize") : PathString.Empty; - settings.LogoutEndpointPath = model.EnableLogoutEndpoint ? - new PathString("/connect/logout") : PathString.Empty; - settings.TokenEndpointPath = model.EnableTokenEndpoint ? - new PathString("/connect/token") : PathString.Empty; - settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ? - new PathString("/connect/userinfo") : PathString.Empty; - settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ? - new PathString("/connect/introspect") : PathString.Empty; - settings.RevocationEndpointPath = model.EnableRevocationEndpoint ? - new PathString("/connect/revoke") : PathString.Empty; + settings.AuthorizationEndpointPath = model.EnableAuthorizationEndpoint ? + new PathString("/connect/authorize") : PathString.Empty; + settings.LogoutEndpointPath = model.EnableLogoutEndpoint ? + new PathString("/connect/logout") : PathString.Empty; + settings.TokenEndpointPath = model.EnableTokenEndpoint ? + new PathString("/connect/token") : PathString.Empty; + settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ? + new PathString("/connect/userinfo") : PathString.Empty; + settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ? + new PathString("/connect/introspect") : PathString.Empty; + settings.RevocationEndpointPath = model.EnableRevocationEndpoint ? + new PathString("/connect/revoke") : PathString.Empty; - settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow; - settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow; - settings.AllowHybridFlow = model.AllowHybridFlow; - settings.AllowImplicitFlow = model.AllowImplicitFlow; - settings.AllowPasswordFlow = model.AllowPasswordFlow; - settings.AllowRefreshTokenFlow = model.AllowRefreshTokenFlow; + settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow; + settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow; + settings.AllowHybridFlow = model.AllowHybridFlow; + settings.AllowImplicitFlow = model.AllowImplicitFlow; + settings.AllowPasswordFlow = model.AllowPasswordFlow; + settings.AllowRefreshTokenFlow = model.AllowRefreshTokenFlow; - settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption; - settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens; - settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens; - settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange; + settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption; + settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens; + settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens; + settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange; - await _serverService.UpdateSettingsAsync(settings); - } + await _serverService.UpdateSettingsAsync(settings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStepModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStepModel.cs index 5693b4b040e..7cfb2ee3206 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStepModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdServerSettingsStepModel.cs @@ -1,35 +1,34 @@ using System.Security.Cryptography.X509Certificates; using static OrchardCore.OpenId.Settings.OpenIdServerSettings; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public class OpenIdServerSettingsStepModel { - public class OpenIdServerSettingsStepModel - { - public TokenFormat AccessTokenFormat { get; set; } = TokenFormat.DataProtection; - [Url] - public string Authority { get; set; } - public bool DisableAccessTokenEncryption { get; set; } - public StoreLocation? EncryptionCertificateStoreLocation { get; set; } - public StoreName? EncryptionCertificateStoreName { get; set; } - public string EncryptionCertificateThumbprint { get; set; } - public StoreLocation? SigningCertificateStoreLocation { get; set; } - public StoreName? SigningCertificateStoreName { get; set; } - public string SigningCertificateThumbprint { get; set; } - public bool EnableTokenEndpoint { get; set; } - public bool EnableAuthorizationEndpoint { get; set; } - public bool EnableLogoutEndpoint { get; set; } - public bool EnableUserInfoEndpoint { get; set; } - public bool EnableIntrospectionEndpoint { get; set; } - public bool EnableRevocationEndpoint { get; set; } - public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } - public bool DisableRollingRefreshTokens { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + public TokenFormat AccessTokenFormat { get; set; } = TokenFormat.DataProtection; + [Url] + public string Authority { get; set; } + public bool DisableAccessTokenEncryption { get; set; } + public StoreLocation? EncryptionCertificateStoreLocation { get; set; } + public StoreName? EncryptionCertificateStoreName { get; set; } + public string EncryptionCertificateThumbprint { get; set; } + public StoreLocation? SigningCertificateStoreLocation { get; set; } + public StoreName? SigningCertificateStoreName { get; set; } + public string SigningCertificateThumbprint { get; set; } + public bool EnableTokenEndpoint { get; set; } + public bool EnableAuthorizationEndpoint { get; set; } + public bool EnableLogoutEndpoint { get; set; } + public bool EnableUserInfoEndpoint { get; set; } + public bool EnableIntrospectionEndpoint { get; set; } + public bool EnableRevocationEndpoint { get; set; } + public bool AllowPasswordFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } + public bool AllowHybridFlow { get; set; } + public bool AllowImplicitFlow { get; set; } + public bool DisableRollingRefreshTokens { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public bool UseReferenceAccessTokens { get; set; } - } + public bool UseReferenceAccessTokens { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStep.cs index f33be3eeb69..2f182ce2d56 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStep.cs @@ -6,37 +6,36 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +/// +/// This recipe step sets Token Validation OpenID Connect settings. +/// +public sealed class OpenIdValidationSettingsStep : IRecipeStepHandler { - /// - /// This recipe step sets Token Validation OpenID Connect settings. - /// - public sealed class OpenIdValidationSettingsStep : IRecipeStepHandler + private readonly IOpenIdValidationService _validationService; + + public OpenIdValidationSettingsStep(IOpenIdValidationService validationService) { - private readonly IOpenIdValidationService _validationService; + _validationService = validationService; + } - public OpenIdValidationSettingsStep(IOpenIdValidationService validationService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, nameof(OpenIdValidationSettings), StringComparison.OrdinalIgnoreCase)) { - _validationService = validationService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, nameof(OpenIdValidationSettings), StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); - var settings = await _validationService.LoadSettingsAsync(); + var model = context.Step.ToObject(); + var settings = await _validationService.LoadSettingsAsync(); - settings.Tenant = model.Tenant; - settings.MetadataAddress = !string.IsNullOrEmpty(model.MetadataAddress) ? new Uri(model.MetadataAddress, UriKind.Absolute) : null; - settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; - settings.Audience = model.Audience; - settings.DisableTokenTypeValidation = model.DisableTokenTypeValidation; + settings.Tenant = model.Tenant; + settings.MetadataAddress = !string.IsNullOrEmpty(model.MetadataAddress) ? new Uri(model.MetadataAddress, UriKind.Absolute) : null; + settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null; + settings.Audience = model.Audience; + settings.DisableTokenTypeValidation = model.DisableTokenTypeValidation; - await _validationService.UpdateSettingsAsync(settings); - } + await _validationService.UpdateSettingsAsync(settings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStepModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStepModel.cs index deb28ced670..026088e321e 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStepModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Recipes/OpenIdValidationSettingsStepModel.cs @@ -1,15 +1,14 @@ -namespace OrchardCore.OpenId.Recipes +namespace OrchardCore.OpenId.Recipes; + +public class OpenIdValidationSettingsStepModel { - public class OpenIdValidationSettingsStepModel - { - public string MetadataAddress { get; set; } + public string MetadataAddress { get; set; } - public string Audience { get; set; } + public string Audience { get; set; } - public string Authority { get; set; } + public string Authority { get; set; } - public bool DisableTokenTypeValidation { get; set; } + public bool DisableTokenTypeValidation { get; set; } - public string Tenant { get; set; } - } + public string Tenant { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdClientService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdClientService.cs index 5f8974be0a9..dc01d183e3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdClientService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdClientService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public interface IOpenIdClientService { - public interface IOpenIdClientService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(OpenIdClientSettings settings); - Task> ValidateSettingsAsync(OpenIdClientSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(OpenIdClientSettings settings); + Task> ValidateSettingsAsync(OpenIdClientSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdServerService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdServerService.cs index 59190710fe8..94637af51d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdServerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdServerService.cs @@ -5,17 +5,16 @@ using Microsoft.IdentityModel.Tokens; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public interface IOpenIdServerService { - public interface IOpenIdServerService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(OpenIdServerSettings settings); - Task> ValidateSettingsAsync(OpenIdServerSettings settings); - Task> GetAvailableCertificatesAsync(); - Task> GetEncryptionKeysAsync(); - Task> GetSigningKeysAsync(); - Task PruneManagedCertificatesAsync(); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(OpenIdServerSettings settings); + Task> ValidateSettingsAsync(OpenIdServerSettings settings); + Task> GetAvailableCertificatesAsync(); + Task> GetEncryptionKeysAsync(); + Task> GetSigningKeysAsync(); + Task PruneManagedCertificatesAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdValidationService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdValidationService.cs index 5b5accde605..0303ac718c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdValidationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/IOpenIdValidationService.cs @@ -3,13 +3,12 @@ using System.Threading.Tasks; using OrchardCore.OpenId.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public interface IOpenIdValidationService { - public interface IOpenIdValidationService - { - Task GetSettingsAsync(); - Task LoadSettingsAsync(); - Task UpdateSettingsAsync(OpenIdValidationSettings settings); - Task> ValidateSettingsAsync(OpenIdValidationSettings settings); - } + Task GetSettingsAsync(); + Task LoadSettingsAsync(); + Task UpdateSettingsAsync(OpenIdValidationSettings settings); + Task> ValidateSettingsAsync(OpenIdValidationSettings settings); } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdClientService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdClientService.cs index 739a33ab9b8..bfed1df4a71 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdClientService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdClientService.cs @@ -8,102 +8,101 @@ using OrchardCore.OpenId.Settings; using OrchardCore.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public class OpenIdClientService : IOpenIdClientService { - public class OpenIdClientService : IOpenIdClientService - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - protected readonly IStringLocalizer S; + protected readonly IStringLocalizer S; - public OpenIdClientService( - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _siteService = siteService; - S = stringLocalizer; - } + public OpenIdClientService( + ISiteService siteService, + IStringLocalizer stringLocalizer) + { + _siteService = siteService; + S = stringLocalizer; + } - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); - public async Task LoadSettingsAsync() - { - var container = await _siteService.LoadSiteSettingsAsync(); - return container.As(); - } + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return container.As(); + } - public async Task UpdateSettingsAsync(OpenIdClientSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + public async Task UpdateSettingsAsync(OpenIdClientSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - var container = await _siteService.LoadSiteSettingsAsync(); - container.Properties[nameof(OpenIdClientSettings)] = JObject.FromObject(settings); - await _siteService.UpdateSiteSettingsAsync(container); - } + var container = await _siteService.LoadSiteSettingsAsync(); + container.Properties[nameof(OpenIdClientSettings)] = JObject.FromObject(settings); + await _siteService.UpdateSiteSettingsAsync(container); + } - public Task> ValidateSettingsAsync(OpenIdClientSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + public Task> ValidateSettingsAsync(OpenIdClientSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - var results = ImmutableArray.CreateBuilder(); + var results = ImmutableArray.CreateBuilder(); - if (settings.Authority == null) + if (settings.Authority == null) + { + results.Add(new ValidationResult(S["The authority cannot be null or empty."], new[] { - results.Add(new ValidationResult(S["The authority cannot be null or empty."], new[] - { - nameof(settings.Authority) - })); - } - else if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) + nameof(settings.Authority) + })); + } + else if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) + { + results.Add(new ValidationResult(S["The authority must be a valid absolute URL."], new[] { - results.Add(new ValidationResult(S["The authority must be a valid absolute URL."], new[] - { - nameof(settings.Authority) - })); - } - else if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) + nameof(settings.Authority) + })); + } + else if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) + { + results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] { - results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] - { - nameof(settings.Authority) - })); - } + nameof(settings.Authority) + })); + } - if (string.IsNullOrEmpty(settings.ResponseType)) + if (string.IsNullOrEmpty(settings.ResponseType)) + { + results.Add(new ValidationResult(S["The response type cannot be null or empty."], new[] { - results.Add(new ValidationResult(S["The response type cannot be null or empty."], new[] - { - nameof(settings.ResponseType) - })); - } - else if (settings.ResponseType != OpenIdConnectResponseType.Code && settings.ResponseType != OpenIdConnectResponseType.CodeIdToken && - settings.ResponseType != OpenIdConnectResponseType.CodeIdTokenToken && settings.ResponseType != OpenIdConnectResponseType.CodeToken && - settings.ResponseType != OpenIdConnectResponseType.IdToken && settings.ResponseType != OpenIdConnectResponseType.IdTokenToken) + nameof(settings.ResponseType) + })); + } + else if (settings.ResponseType != OpenIdConnectResponseType.Code && settings.ResponseType != OpenIdConnectResponseType.CodeIdToken && + settings.ResponseType != OpenIdConnectResponseType.CodeIdTokenToken && settings.ResponseType != OpenIdConnectResponseType.CodeToken && + settings.ResponseType != OpenIdConnectResponseType.IdToken && settings.ResponseType != OpenIdConnectResponseType.IdTokenToken) + { + results.Add(new ValidationResult(S["Unknown response type ."], new[] { - results.Add(new ValidationResult(S["Unknown response type ."], new[] - { - nameof(settings.ResponseType) - })); - } + nameof(settings.ResponseType) + })); + } - if (string.IsNullOrEmpty(settings.ResponseMode)) + if (string.IsNullOrEmpty(settings.ResponseMode)) + { + results.Add(new ValidationResult(S["The response mode cannot be null or empty."], new[] { - results.Add(new ValidationResult(S["The response mode cannot be null or empty."], new[] - { - nameof(settings.ResponseMode) - })); - } - else if (settings.ResponseMode != OpenIdConnectResponseMode.FormPost && settings.ResponseMode != OpenIdConnectResponseMode.Fragment && - settings.ResponseMode != OpenIdConnectResponseMode.Query) + nameof(settings.ResponseMode) + })); + } + else if (settings.ResponseMode != OpenIdConnectResponseMode.FormPost && settings.ResponseMode != OpenIdConnectResponseMode.Fragment && + settings.ResponseMode != OpenIdConnectResponseMode.Query) + { + results.Add(new ValidationResult(S["Unknown response mode."], new[] { - results.Add(new ValidationResult(S["Unknown response mode."], new[] - { - nameof(settings.ResponseMode) - })); - } - - return Task.FromResult(results.ToImmutable()); + nameof(settings.ResponseMode) + })); } + + return Task.FromResult(results.ToImmutable()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdServerService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdServerService.cs index f555ff5a902..ba3649be61b 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdServerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdServerService.cs @@ -20,623 +20,622 @@ using OrchardCore.OpenId.Settings; using OrchardCore.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public class OpenIdServerService : IOpenIdServerService { - public class OpenIdServerService : IOpenIdServerService + private readonly IDataProtector _dataProtector; + private readonly ILogger _logger; + private readonly IMemoryCache _memoryCache; + private readonly IOptionsMonitor _shellOptions; + private readonly ShellSettings _shellSettings; + private readonly ISiteService _siteService; + + protected readonly IStringLocalizer S; + + public OpenIdServerService( + IDataProtectionProvider dataProtectionProvider, + ILogger logger, + IMemoryCache memoryCache, + IOptionsMonitor shellOptions, + ShellSettings shellSettings, + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly IDataProtector _dataProtector; - private readonly ILogger _logger; - private readonly IMemoryCache _memoryCache; - private readonly IOptionsMonitor _shellOptions; - private readonly ShellSettings _shellSettings; - private readonly ISiteService _siteService; - - protected readonly IStringLocalizer S; - - public OpenIdServerService( - IDataProtectionProvider dataProtectionProvider, - ILogger logger, - IMemoryCache memoryCache, - IOptionsMonitor shellOptions, - ShellSettings shellSettings, - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _dataProtector = dataProtectionProvider.CreateProtector(nameof(OpenIdServerService)); - _logger = logger; - _memoryCache = memoryCache; - _shellOptions = shellOptions; - _shellSettings = shellSettings; - _siteService = siteService; - S = stringLocalizer; - } + _dataProtector = dataProtectionProvider.CreateProtector(nameof(OpenIdServerService)); + _logger = logger; + _memoryCache = memoryCache; + _shellOptions = shellOptions; + _shellSettings = shellSettings; + _siteService = siteService; + S = stringLocalizer; + } - public async Task GetSettingsAsync() - { - var container = await _siteService.GetSiteSettingsAsync(); - return GetSettingsFromContainer(container); - } + public async Task GetSettingsAsync() + { + var container = await _siteService.GetSiteSettingsAsync(); + return GetSettingsFromContainer(container); + } - public async Task LoadSettingsAsync() + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return GetSettingsFromContainer(container); + } + + private OpenIdServerSettings GetSettingsFromContainer(ISite container) + { + if (container.Properties.TryGetPropertyValue(nameof(OpenIdServerSettings), out var settings)) { - var container = await _siteService.LoadSiteSettingsAsync(); - return GetSettingsFromContainer(container); + return settings.ToObject(JOptions.Default); } - private OpenIdServerSettings GetSettingsFromContainer(ISite container) + // If the OpenID server settings haven't been populated yet, the authorization, + // logout, token, userinfo, introspection and revocation endpoints are assumed to be enabled by default. + // In this case, only the authorization code and refresh token flows are used. + return new OpenIdServerSettings { - if (container.Properties.TryGetPropertyValue(nameof(OpenIdServerSettings), out var settings)) - { - return settings.ToObject(JOptions.Default); - } + AllowAuthorizationCodeFlow = true, + AllowRefreshTokenFlow = true, + AuthorizationEndpointPath = "/connect/authorize", + LogoutEndpointPath = "/connect/logout", + TokenEndpointPath = "/connect/token", + UserinfoEndpointPath = "/connect/userinfo", + IntrospectionEndpointPath = "/connect/introspect", + RevocationEndpointPath = "/connect/revoke" + }; + } - // If the OpenID server settings haven't been populated yet, the authorization, - // logout, token, userinfo, introspection and revocation endpoints are assumed to be enabled by default. - // In this case, only the authorization code and refresh token flows are used. - return new OpenIdServerSettings - { - AllowAuthorizationCodeFlow = true, - AllowRefreshTokenFlow = true, - AuthorizationEndpointPath = "/connect/authorize", - LogoutEndpointPath = "/connect/logout", - TokenEndpointPath = "/connect/token", - UserinfoEndpointPath = "/connect/userinfo", - IntrospectionEndpointPath = "/connect/introspect", - RevocationEndpointPath = "/connect/revoke" - }; - } + public async Task UpdateSettingsAsync(OpenIdServerSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(OpenIdServerSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + var container = await _siteService.LoadSiteSettingsAsync(); + container.Properties[nameof(OpenIdServerSettings)] = JObject.FromObject(settings, JOptions.Default); + await _siteService.UpdateSiteSettingsAsync(container); + } - var container = await _siteService.LoadSiteSettingsAsync(); - container.Properties[nameof(OpenIdServerSettings)] = JObject.FromObject(settings, JOptions.Default); - await _siteService.UpdateSiteSettingsAsync(container); - } + public Task> ValidateSettingsAsync(OpenIdServerSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public Task> ValidateSettingsAsync(OpenIdServerSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + var results = ImmutableArray.CreateBuilder(); - var results = ImmutableArray.CreateBuilder(); + if (!settings.AllowAuthorizationCodeFlow && !settings.AllowClientCredentialsFlow && + !settings.AllowHybridFlow && !settings.AllowImplicitFlow && + !settings.AllowPasswordFlow && !settings.AllowRefreshTokenFlow) + { + results.Add(new ValidationResult(S["At least one OpenID Connect flow must be enabled."])); + } - if (!settings.AllowAuthorizationCodeFlow && !settings.AllowClientCredentialsFlow && - !settings.AllowHybridFlow && !settings.AllowImplicitFlow && - !settings.AllowPasswordFlow && !settings.AllowRefreshTokenFlow) + if (settings.Authority != null) + { + if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) { - results.Add(new ValidationResult(S["At least one OpenID Connect flow must be enabled."])); + results.Add(new ValidationResult(S["The authority must be a valid absolute URL."], new[] + { + nameof(settings.Authority) + })); } - if (settings.Authority != null) + if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) { - if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) - { - results.Add(new ValidationResult(S["The authority must be a valid absolute URL."], new[] - { - nameof(settings.Authority) - })); - } - - if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) + results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] { - results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] - { - nameof(settings.Authority) - })); - } + nameof(settings.Authority) + })); } + } - if (settings.SigningCertificateStoreLocation != null && - settings.SigningCertificateStoreName != null && - !string.IsNullOrEmpty(settings.SigningCertificateThumbprint)) - { - var certificate = GetCertificate( - settings.SigningCertificateStoreLocation.Value, - settings.SigningCertificateStoreName.Value, settings.SigningCertificateThumbprint); + if (settings.SigningCertificateStoreLocation != null && + settings.SigningCertificateStoreName != null && + !string.IsNullOrEmpty(settings.SigningCertificateThumbprint)) + { + var certificate = GetCertificate( + settings.SigningCertificateStoreLocation.Value, + settings.SigningCertificateStoreName.Value, settings.SigningCertificateThumbprint); - if (certificate == null) - { - results.Add(new ValidationResult(S["The certificate cannot be found."], new[] - { - nameof(settings.SigningCertificateThumbprint) - })); - } - else if (!certificate.HasPrivateKey) - { - results.Add(new ValidationResult(S["The certificate doesn't contain the required private key."], new[] - { - nameof(settings.SigningCertificateThumbprint) - })); - } - else if (certificate.Archived) - { - results.Add(new ValidationResult(S["The certificate is not valid because it is marked as archived."], new[] - { - nameof(settings.SigningCertificateThumbprint) - })); - } - else if (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now) + if (certificate == null) + { + results.Add(new ValidationResult(S["The certificate cannot be found."], new[] { - results.Add(new ValidationResult(S["The certificate is not valid for current date."], new[] - { - nameof(settings.SigningCertificateThumbprint) - })); - } + nameof(settings.SigningCertificateThumbprint) + })); } - - if (settings.AllowPasswordFlow && !settings.TokenEndpointPath.HasValue) + else if (!certificate.HasPrivateKey) { - results.Add(new ValidationResult(S["The password flow cannot be enabled when the token endpoint is disabled."], new[] + results.Add(new ValidationResult(S["The certificate doesn't contain the required private key."], new[] { - nameof(settings.AllowPasswordFlow) + nameof(settings.SigningCertificateThumbprint) })); } - - if (settings.AllowClientCredentialsFlow && !settings.TokenEndpointPath.HasValue) + else if (certificate.Archived) { - results.Add(new ValidationResult(S["The client credentials flow cannot be enabled when the token endpoint is disabled."], new[] + results.Add(new ValidationResult(S["The certificate is not valid because it is marked as archived."], new[] { - nameof(settings.AllowClientCredentialsFlow) + nameof(settings.SigningCertificateThumbprint) })); } - - if (settings.AllowAuthorizationCodeFlow && (!settings.AuthorizationEndpointPath.HasValue || !settings.TokenEndpointPath.HasValue)) + else if (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now) { - results.Add(new ValidationResult(S["The authorization code flow cannot be enabled when the authorization and token endpoints are disabled."], new[] + results.Add(new ValidationResult(S["The certificate is not valid for current date."], new[] { - nameof(settings.AllowAuthorizationCodeFlow) + nameof(settings.SigningCertificateThumbprint) })); } + } - if (settings.AllowRefreshTokenFlow) + if (settings.AllowPasswordFlow && !settings.TokenEndpointPath.HasValue) + { + results.Add(new ValidationResult(S["The password flow cannot be enabled when the token endpoint is disabled."], new[] { - if (!settings.TokenEndpointPath.HasValue) - { - results.Add(new ValidationResult(S["The refresh token flow cannot be enabled when the token endpoint is disabled."], new[] - { - nameof(settings.AllowRefreshTokenFlow) - })); - } + nameof(settings.AllowPasswordFlow) + })); + } - if (!settings.AllowPasswordFlow && !settings.AllowAuthorizationCodeFlow && !settings.AllowHybridFlow) - { - results.Add(new ValidationResult(S["The refresh token flow can only be enabled if the password, authorization code or hybrid flows are enabled."], new[] - { - nameof(settings.AllowRefreshTokenFlow) - })); - } - } + if (settings.AllowClientCredentialsFlow && !settings.TokenEndpointPath.HasValue) + { + results.Add(new ValidationResult(S["The client credentials flow cannot be enabled when the token endpoint is disabled."], new[] + { + nameof(settings.AllowClientCredentialsFlow) + })); + } - if (settings.AllowImplicitFlow && !settings.AuthorizationEndpointPath.HasValue) + if (settings.AllowAuthorizationCodeFlow && (!settings.AuthorizationEndpointPath.HasValue || !settings.TokenEndpointPath.HasValue)) + { + results.Add(new ValidationResult(S["The authorization code flow cannot be enabled when the authorization and token endpoints are disabled."], new[] { - results.Add(new ValidationResult(S["The implicit flow cannot be enabled when the authorization endpoint is disabled."], new[] - { - nameof(settings.AllowImplicitFlow) - })); - } + nameof(settings.AllowAuthorizationCodeFlow) + })); + } - if (settings.AllowHybridFlow && (!settings.AuthorizationEndpointPath.HasValue || !settings.TokenEndpointPath.HasValue)) + if (settings.AllowRefreshTokenFlow) + { + if (!settings.TokenEndpointPath.HasValue) { - results.Add(new ValidationResult(S["The hybrid flow cannot be enabled when the authorization and token endpoints are disabled."], new[] + results.Add(new ValidationResult(S["The refresh token flow cannot be enabled when the token endpoint is disabled."], new[] { - nameof(settings.AllowHybridFlow) + nameof(settings.AllowRefreshTokenFlow) })); } - if (settings.DisableAccessTokenEncryption && settings.AccessTokenFormat != OpenIdServerSettings.TokenFormat.JsonWebToken) + if (!settings.AllowPasswordFlow && !settings.AllowAuthorizationCodeFlow && !settings.AllowHybridFlow) { - results.Add(new ValidationResult(S["Access token encryption can only be disabled when using JWT tokens."], new[] + results.Add(new ValidationResult(S["The refresh token flow can only be enabled if the password, authorization code or hybrid flows are enabled."], new[] { - nameof(settings.DisableAccessTokenEncryption) + nameof(settings.AllowRefreshTokenFlow) })); } + } + + if (settings.AllowImplicitFlow && !settings.AuthorizationEndpointPath.HasValue) + { + results.Add(new ValidationResult(S["The implicit flow cannot be enabled when the authorization endpoint is disabled."], new[] + { + nameof(settings.AllowImplicitFlow) + })); + } - return Task.FromResult(results.ToImmutable()); + if (settings.AllowHybridFlow && (!settings.AuthorizationEndpointPath.HasValue || !settings.TokenEndpointPath.HasValue)) + { + results.Add(new ValidationResult(S["The hybrid flow cannot be enabled when the authorization and token endpoints are disabled."], new[] + { + nameof(settings.AllowHybridFlow) + })); } - public Task> GetAvailableCertificatesAsync() + if (settings.DisableAccessTokenEncryption && settings.AccessTokenFormat != OpenIdServerSettings.TokenFormat.JsonWebToken) { - var certificates = ImmutableArray.CreateBuilder<(X509Certificate2, StoreLocation, StoreName)>(); + results.Add(new ValidationResult(S["Access token encryption can only be disabled when using JWT tokens."], new[] + { + nameof(settings.DisableAccessTokenEncryption) + })); + } + + return Task.FromResult(results.ToImmutable()); + } - foreach (StoreLocation location in Enum.GetValues()) + public Task> GetAvailableCertificatesAsync() + { + var certificates = ImmutableArray.CreateBuilder<(X509Certificate2, StoreLocation, StoreName)>(); + + foreach (StoreLocation location in Enum.GetValues()) + { + foreach (StoreName name in Enum.GetValues()) { - foreach (StoreName name in Enum.GetValues()) + // Note: on non-Windows platforms, an exception can + // be thrown if the store location/name doesn't exist. + try { - // Note: on non-Windows platforms, an exception can - // be thrown if the store location/name doesn't exist. - try - { - using var store = new X509Store(name, location); - store.Open(OpenFlags.ReadOnly); + using var store = new X509Store(name, location); + store.Open(OpenFlags.ReadOnly); - foreach (var certificate in store.Certificates) + foreach (var certificate in store.Certificates) + { + if (!certificate.Archived && certificate.HasPrivateKey) { - if (!certificate.Archived && certificate.HasPrivateKey) - { - certificates.Add((certificate, location, name)); - } + certificates.Add((certificate, location, name)); } } - catch (CryptographicException) - { - continue; - } + } + catch (CryptographicException) + { + continue; } } - - return Task.FromResult(certificates.ToImmutable()); } - public async Task> GetEncryptionKeysAsync() + return Task.FromResult(certificates.ToImmutable()); + } + + public async Task> GetEncryptionKeysAsync() + { + var settings = await GetSettingsAsync(); + + // If a certificate was explicitly provided, return it immediately + // instead of using the fallback managed certificates logic. + if (settings.EncryptionCertificateStoreLocation != null && + settings.EncryptionCertificateStoreName != null && + !string.IsNullOrEmpty(settings.EncryptionCertificateThumbprint)) { - var settings = await GetSettingsAsync(); + var certificate = GetCertificate( + settings.EncryptionCertificateStoreLocation.Value, + settings.EncryptionCertificateStoreName.Value, settings.EncryptionCertificateThumbprint); - // If a certificate was explicitly provided, return it immediately - // instead of using the fallback managed certificates logic. - if (settings.EncryptionCertificateStoreLocation != null && - settings.EncryptionCertificateStoreName != null && - !string.IsNullOrEmpty(settings.EncryptionCertificateThumbprint)) + if (certificate != null) { - var certificate = GetCertificate( - settings.EncryptionCertificateStoreLocation.Value, - settings.EncryptionCertificateStoreName.Value, settings.EncryptionCertificateThumbprint); + return [new X509SecurityKey(certificate)]; + } - if (certificate != null) - { - return [new X509SecurityKey(certificate)]; - } + _logger.LogWarning("The encryption certificate '{Thumbprint}' could not be found in the " + + "{StoreLocation}/{StoreName} store.", settings.EncryptionCertificateThumbprint, + settings.EncryptionCertificateStoreLocation.Value.ToString(), + settings.EncryptionCertificateStoreName.Value.ToString()); + } + + try + { + var directory = GetEncryptionCertificateDirectory(_shellOptions.CurrentValue, _shellSettings); - _logger.LogWarning("The encryption certificate '{Thumbprint}' could not be found in the " + - "{StoreLocation}/{StoreName} store.", settings.EncryptionCertificateThumbprint, - settings.EncryptionCertificateStoreLocation.Value.ToString(), - settings.EncryptionCertificateStoreName.Value.ToString()); + var certificates = (await GetCertificatesAsync(directory)).Select(tuple => tuple.certificate).ToList(); + if (certificates.Any(certificate => certificate.NotAfter.AddDays(-7) > DateTime.Now)) + { + return ImmutableArray.CreateRange( + from certificate in certificates + select new X509SecurityKey(certificate)); } try { - var directory = GetEncryptionCertificateDirectory(_shellOptions.CurrentValue, _shellSettings); - - var certificates = (await GetCertificatesAsync(directory)).Select(tuple => tuple.certificate).ToList(); - if (certificates.Any(certificate => certificate.NotAfter.AddDays(-7) > DateTime.Now)) - { - return ImmutableArray.CreateRange( - from certificate in certificates - select new X509SecurityKey(certificate)); - } - - try - { - // If the certificates list is empty or only contains certificates about to expire, - // generate a new certificate and add it on top of the list to ensure it's preferred - // by OpenIddict to the other certificates when issuing new IdentityModel tokens. - var certificate = GenerateEncryptionCertificate(_shellSettings); - await PersistCertificateAsync(directory, certificate); + // If the certificates list is empty or only contains certificates about to expire, + // generate a new certificate and add it on top of the list to ensure it's preferred + // by OpenIddict to the other certificates when issuing new IdentityModel tokens. + var certificate = GenerateEncryptionCertificate(_shellSettings); + await PersistCertificateAsync(directory, certificate); - certificates.Insert(0, certificate); + certificates.Insert(0, certificate); - return certificates.Select(certificate => new X509SecurityKey(certificate)).ToImmutableArray(); - } - catch (Exception exception) - { - _logger.LogError(exception, "An error occurred while trying to generate a X.509 encryption certificate."); - } + return certificates.Select(certificate => new X509SecurityKey(certificate)).ToImmutableArray(); } catch (Exception exception) { - _logger.LogWarning(exception, "An error occurred while trying to retrieve the X.509 encryption certificates."); + _logger.LogError(exception, "An error occurred while trying to generate a X.509 encryption certificate."); } + } + catch (Exception exception) + { + _logger.LogWarning(exception, "An error occurred while trying to retrieve the X.509 encryption certificates."); + } - // If none of the previous attempts succeeded, try to generate an ephemeral RSA key - // and add it in the tenant memory cache so that future calls to this method return it. - return - [ - _memoryCache.GetOrCreate("05A24221-8C15-4E58-A0A7-56EC3E42E783", entry => - { - entry.SetPriority(CacheItemPriority.NeverRemove); + // If none of the previous attempts succeeded, try to generate an ephemeral RSA key + // and add it in the tenant memory cache so that future calls to this method return it. + return + [ + _memoryCache.GetOrCreate("05A24221-8C15-4E58-A0A7-56EC3E42E783", entry => + { + entry.SetPriority(CacheItemPriority.NeverRemove); - return new RsaSecurityKey(GenerateRsaSecurityKey(size: 2048)); - }), - ]; - } + return new RsaSecurityKey(GenerateRsaSecurityKey(size: 2048)); + }), + ]; + } - public async Task> GetSigningKeysAsync() + public async Task> GetSigningKeysAsync() + { + var settings = await GetSettingsAsync(); + + // If a certificate was explicitly provided, return it immediately + // instead of using the fallback managed certificates logic. + if (settings.SigningCertificateStoreLocation != null && + settings.SigningCertificateStoreName != null && + !string.IsNullOrEmpty(settings.SigningCertificateThumbprint)) { - var settings = await GetSettingsAsync(); + var certificate = GetCertificate( + settings.SigningCertificateStoreLocation.Value, + settings.SigningCertificateStoreName.Value, settings.SigningCertificateThumbprint); - // If a certificate was explicitly provided, return it immediately - // instead of using the fallback managed certificates logic. - if (settings.SigningCertificateStoreLocation != null && - settings.SigningCertificateStoreName != null && - !string.IsNullOrEmpty(settings.SigningCertificateThumbprint)) + if (certificate != null) { - var certificate = GetCertificate( - settings.SigningCertificateStoreLocation.Value, - settings.SigningCertificateStoreName.Value, settings.SigningCertificateThumbprint); + return [new X509SecurityKey(certificate)]; + } - if (certificate != null) - { - return [new X509SecurityKey(certificate)]; - } + _logger.LogWarning("The signing certificate '{Thumbprint}' could not be found in the " + + "{StoreLocation}/{StoreName} store.", settings.SigningCertificateThumbprint, + settings.SigningCertificateStoreLocation.Value.ToString(), + settings.SigningCertificateStoreName.Value.ToString()); + } + + try + { + var directory = GetSigningCertificateDirectory(_shellOptions.CurrentValue, _shellSettings); - _logger.LogWarning("The signing certificate '{Thumbprint}' could not be found in the " + - "{StoreLocation}/{StoreName} store.", settings.SigningCertificateThumbprint, - settings.SigningCertificateStoreLocation.Value.ToString(), - settings.SigningCertificateStoreName.Value.ToString()); + var certificates = (await GetCertificatesAsync(directory)).Select(tuple => tuple.certificate).ToList(); + if (certificates.Any(certificate => certificate.NotAfter.AddDays(-7) > DateTime.Now)) + { + return ImmutableArray.CreateRange( + from certificate in certificates + select new X509SecurityKey(certificate)); } try { - var directory = GetSigningCertificateDirectory(_shellOptions.CurrentValue, _shellSettings); - - var certificates = (await GetCertificatesAsync(directory)).Select(tuple => tuple.certificate).ToList(); - if (certificates.Any(certificate => certificate.NotAfter.AddDays(-7) > DateTime.Now)) - { - return ImmutableArray.CreateRange( - from certificate in certificates - select new X509SecurityKey(certificate)); - } - - try - { - // If the certificates list is empty or only contains certificates about to expire, - // generate a new certificate and add it on top of the list to ensure it's preferred - // by OpenIddict to the other certificates when issuing new IdentityModel tokens. - var certificate = GenerateSigningCertificate(_shellSettings); - await PersistCertificateAsync(directory, certificate); + // If the certificates list is empty or only contains certificates about to expire, + // generate a new certificate and add it on top of the list to ensure it's preferred + // by OpenIddict to the other certificates when issuing new IdentityModel tokens. + var certificate = GenerateSigningCertificate(_shellSettings); + await PersistCertificateAsync(directory, certificate); - certificates.Insert(0, certificate); + certificates.Insert(0, certificate); - return certificates.Select(certificate => new X509SecurityKey(certificate)).ToImmutableArray(); - } - catch (Exception exception) - { - _logger.LogError(exception, "An error occurred while trying to generate a X.509 signing certificate."); - } + return certificates.Select(certificate => new X509SecurityKey(certificate)).ToImmutableArray(); } catch (Exception exception) { - _logger.LogWarning(exception, "An error occurred while trying to retrieve the X.509 signing certificates."); + _logger.LogError(exception, "An error occurred while trying to generate a X.509 signing certificate."); } + } + catch (Exception exception) + { + _logger.LogWarning(exception, "An error occurred while trying to retrieve the X.509 signing certificates."); + } - // If none of the previous attempts succeeded, try to generate an ephemeral RSA key - // and add it in the tenant memory cache so that future calls to this method return it. - return - [ - _memoryCache.GetOrCreate("44788774-20E3-4499-86F0-AB7CE2DF97F6", entry => - { - entry.SetPriority(CacheItemPriority.NeverRemove); + // If none of the previous attempts succeeded, try to generate an ephemeral RSA key + // and add it in the tenant memory cache so that future calls to this method return it. + return + [ + _memoryCache.GetOrCreate("44788774-20E3-4499-86F0-AB7CE2DF97F6", entry => + { + entry.SetPriority(CacheItemPriority.NeverRemove); - return new RsaSecurityKey(GenerateRsaSecurityKey(size: 2048)); - }), - ]; - } + return new RsaSecurityKey(GenerateRsaSecurityKey(size: 2048)); + }), + ]; + } - public async Task PruneManagedCertificatesAsync() - { - List exceptions = null; + public async Task PruneManagedCertificatesAsync() + { + List exceptions = null; - var certificates = new List<(string path, X509Certificate2 certificate)>(); - certificates.AddRange(await GetCertificatesAsync(GetEncryptionCertificateDirectory(_shellOptions.CurrentValue, _shellSettings))); - certificates.AddRange(await GetCertificatesAsync(GetSigningCertificateDirectory(_shellOptions.CurrentValue, _shellSettings))); + var certificates = new List<(string path, X509Certificate2 certificate)>(); + certificates.AddRange(await GetCertificatesAsync(GetEncryptionCertificateDirectory(_shellOptions.CurrentValue, _shellSettings))); + certificates.AddRange(await GetCertificatesAsync(GetSigningCertificateDirectory(_shellOptions.CurrentValue, _shellSettings))); - foreach (var (path, certificate) in certificates) + foreach (var (path, certificate) in certificates) + { + // Only delete expired certificates that expired at least 7 days ago. + if (certificate.NotAfter.AddDays(7) < DateTime.Now) { - // Only delete expired certificates that expired at least 7 days ago. - if (certificate.NotAfter.AddDays(7) < DateTime.Now) + try { - try - { - // Delete both the X.509 certificate and its password file. - File.Delete(path); - File.Delete(Path.ChangeExtension(path, ".pwd")); - } - catch (Exception exception) - { - exceptions ??= []; - - exceptions.Add(exception); - } + // Delete both the X.509 certificate and its password file. + File.Delete(path); + File.Delete(Path.ChangeExtension(path, ".pwd")); } - } + catch (Exception exception) + { + exceptions ??= []; - if (exceptions != null) - { - throw new AggregateException(exceptions); + exceptions.Add(exception); + } } } - private static X509Certificate2 GetCertificate(StoreLocation location, StoreName name, string thumbprint) + if (exceptions != null) { - using var store = new X509Store(name, location); - store.Open(OpenFlags.ReadOnly); + throw new AggregateException(exceptions); + } + } - var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false); + private static X509Certificate2 GetCertificate(StoreLocation location, StoreName name, string thumbprint) + { + using var store = new X509Store(name, location); + store.Open(OpenFlags.ReadOnly); - return certificates.Count switch - { - 0 => null, - 1 => certificates[0], - _ => throw new InvalidOperationException("Multiple certificates with the same thumbprint were found."), - }; - } + var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false); - private static DirectoryInfo GetEncryptionCertificateDirectory(ShellOptions options, ShellSettings settings) - => Directory.CreateDirectory(Path.Combine( - options.ShellsApplicationDataPath, - options.ShellsContainerName, - settings.Name, "IdentityModel-Encryption-Certificates")); + return certificates.Count switch + { + 0 => null, + 1 => certificates[0], + _ => throw new InvalidOperationException("Multiple certificates with the same thumbprint were found."), + }; + } - private static DirectoryInfo GetSigningCertificateDirectory(ShellOptions options, ShellSettings settings) - => Directory.CreateDirectory(Path.Combine( - options.ShellsApplicationDataPath, - options.ShellsContainerName, - settings.Name, "IdentityModel-Signing-Certificates")); + private static DirectoryInfo GetEncryptionCertificateDirectory(ShellOptions options, ShellSettings settings) + => Directory.CreateDirectory(Path.Combine( + options.ShellsApplicationDataPath, + options.ShellsContainerName, + settings.Name, "IdentityModel-Encryption-Certificates")); - private async Task> GetCertificatesAsync(DirectoryInfo directory) + private static DirectoryInfo GetSigningCertificateDirectory(ShellOptions options, ShellSettings settings) + => Directory.CreateDirectory(Path.Combine( + options.ShellsApplicationDataPath, + options.ShellsContainerName, + settings.Name, "IdentityModel-Signing-Certificates")); + + private async Task> GetCertificatesAsync(DirectoryInfo directory) + { + if (!directory.Exists) { - if (!directory.Exists) - { - return ImmutableArray.Create<(string, X509Certificate2)>(); - } + return ImmutableArray.Create<(string, X509Certificate2)>(); + } - var certificates = ImmutableArray.CreateBuilder<(string, X509Certificate2)>(); + var certificates = ImmutableArray.CreateBuilder<(string, X509Certificate2)>(); - foreach (var file in directory.EnumerateFiles("*.pfx", SearchOption.TopDirectoryOnly)) + foreach (var file in directory.EnumerateFiles("*.pfx", SearchOption.TopDirectoryOnly)) + { + try { - try + // Only add the certificate if it's still valid. + var certificate = await GetCertificateAsync(file.FullName); + if (certificate.NotBefore <= DateTime.Now && certificate.NotAfter > DateTime.Now) { - // Only add the certificate if it's still valid. - var certificate = await GetCertificateAsync(file.FullName); - if (certificate.NotBefore <= DateTime.Now && certificate.NotAfter > DateTime.Now) - { - certificates.Add((file.FullName, certificate)); - } - } - catch (Exception exception) - { - _logger.LogWarning(exception, "An error occurred while trying to extract a X.509 certificate."); - - continue; + certificates.Add((file.FullName, certificate)); } } - - return certificates.ToImmutable(); - - async Task GetPasswordAsync(string path) + catch (Exception exception) { - using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); - using var reader = new StreamReader(stream); + _logger.LogWarning(exception, "An error occurred while trying to extract a X.509 certificate."); - return _dataProtector.Unprotect(await reader.ReadToEndAsync()); + continue; } + } - async Task GetCertificateAsync(string path) - { - // Extract the certificate password from the separate .pwd file. - var password = await GetPasswordAsync(Path.ChangeExtension(path, ".pwd")); + return certificates.ToImmutable(); - try - { - // Note: ephemeral key sets are not supported on non-Windows platforms. - var flags = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - X509KeyStorageFlags.EphemeralKeySet : - X509KeyStorageFlags.MachineKeySet; - - return new X509Certificate2(path, password, flags); - } - // Some cloud platforms (e.g Azure App Service/Antares) are known to fail to import .pfx files if the - // private key is not persisted or marked as exportable. To ensure X.509 certificates can be correctly - // read on these platforms, a second pass is made by specifying the PersistKeySet and Exportable flags. - // For more information, visit https://github.com/OrchardCMS/OrchardCore/issues/3222. - catch (CryptographicException exception) - { - _logger.LogDebug(exception, "A first-chance exception occurred while trying to extract " + - "a X.509 certificate with the default key storage options."); + async Task GetPasswordAsync(string path) + { + using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read); + using var reader = new StreamReader(stream); - return new X509Certificate2(path, password, - X509KeyStorageFlags.MachineKeySet | - X509KeyStorageFlags.PersistKeySet | - X509KeyStorageFlags.Exportable); - } - // Don't swallow exceptions thrown from the catch handler to ensure unrecoverable exceptions - // (e.g caused by malformed X.509 certificates or invalid password) are correctly logged. - } + return _dataProtector.Unprotect(await reader.ReadToEndAsync()); } - private static X509Certificate2 GenerateEncryptionCertificate(ShellSettings settings) + async Task GetCertificateAsync(string path) { - var algorithm = GenerateRsaSecurityKey(size: 2048); - var certificate = GenerateCertificate(X509KeyUsageFlags.KeyEncipherment, algorithm, settings); + // Extract the certificate password from the separate .pwd file. + var password = await GetPasswordAsync(Path.ChangeExtension(path, ".pwd")); - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + try { - certificate.FriendlyName = "OrchardCore OpenID Server Encryption Certificate"; + // Note: ephemeral key sets are not supported on non-Windows platforms. + var flags = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + X509KeyStorageFlags.EphemeralKeySet : + X509KeyStorageFlags.MachineKeySet; + + return new X509Certificate2(path, password, flags); } + // Some cloud platforms (e.g Azure App Service/Antares) are known to fail to import .pfx files if the + // private key is not persisted or marked as exportable. To ensure X.509 certificates can be correctly + // read on these platforms, a second pass is made by specifying the PersistKeySet and Exportable flags. + // For more information, visit https://github.com/OrchardCMS/OrchardCore/issues/3222. + catch (CryptographicException exception) + { + _logger.LogDebug(exception, "A first-chance exception occurred while trying to extract " + + "a X.509 certificate with the default key storage options."); - return certificate; + return new X509Certificate2(path, password, + X509KeyStorageFlags.MachineKeySet | + X509KeyStorageFlags.PersistKeySet | + X509KeyStorageFlags.Exportable); + } + // Don't swallow exceptions thrown from the catch handler to ensure unrecoverable exceptions + // (e.g caused by malformed X.509 certificates or invalid password) are correctly logged. } + } + + private static X509Certificate2 GenerateEncryptionCertificate(ShellSettings settings) + { + var algorithm = GenerateRsaSecurityKey(size: 2048); + var certificate = GenerateCertificate(X509KeyUsageFlags.KeyEncipherment, algorithm, settings); - private static X509Certificate2 GenerateSigningCertificate(ShellSettings settings) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var algorithm = GenerateRsaSecurityKey(size: 2048); - var certificate = GenerateCertificate(X509KeyUsageFlags.DigitalSignature, algorithm, settings); + certificate.FriendlyName = "OrchardCore OpenID Server Encryption Certificate"; + } - // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). - // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - certificate.FriendlyName = "OrchardCore OpenID Server Signing Certificate"; - } + return certificate; + } - return certificate; - } + private static X509Certificate2 GenerateSigningCertificate(ShellSettings settings) + { + var algorithm = GenerateRsaSecurityKey(size: 2048); + var certificate = GenerateCertificate(X509KeyUsageFlags.DigitalSignature, algorithm, settings); - private static X509Certificate2 GenerateCertificate(X509KeyUsageFlags type, RSA algorithm, ShellSettings settings) + // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). + // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - var subject = GetSubjectName(); + certificate.FriendlyName = "OrchardCore OpenID Server Signing Certificate"; + } + + return certificate; + } + + private static X509Certificate2 GenerateCertificate(X509KeyUsageFlags type, RSA algorithm, ShellSettings settings) + { + var subject = GetSubjectName(); - // Note: ensure the digitalSignature bit is added to the certificate, so that no validation error - // is returned to clients that fully validate the certificates chain and their X.509 key usages. - var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - request.CertificateExtensions.Add(new X509KeyUsageExtension(type, critical: true)); + // Note: ensure the digitalSignature bit is added to the certificate, so that no validation error + // is returned to clients that fully validate the certificates chain and their X.509 key usages. + var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509KeyUsageExtension(type, critical: true)); - return request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddMonths(3)); + return request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddMonths(3)); - X500DistinguishedName GetSubjectName() + X500DistinguishedName GetSubjectName() + { + var host = settings.RequestUrlHosts.FirstOrDefault(host => host != "localhost"); + try { - var host = settings.RequestUrlHosts.FirstOrDefault(host => host != "localhost"); - try - { - return new X500DistinguishedName("CN=" + (host ?? "localhost")); - } - catch - { - return new X500DistinguishedName("CN=localhost"); - } + return new X500DistinguishedName("CN=" + (host ?? "localhost")); + } + catch + { + return new X500DistinguishedName("CN=localhost"); } } + } - private static RSA GenerateRsaSecurityKey(int size) + private static RSA GenerateRsaSecurityKey(int size) + { + // By default, the default RSA implementation used by .NET Core relies on the newest Windows CNG APIs. + // Unfortunately, when a new key is generated using the default RSA.Create() method, it is not bound + // to the machine account, which may cause security exceptions when running Orchard on IIS using a + // virtual application pool identity or without the profile loading feature enabled (off by default). + // To ensure a RSA key can be generated flawlessly, it is manually created using the managed CNG APIs. + // For more information, visit https://github.com/openiddict/openiddict-core/issues/204. + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - // By default, the default RSA implementation used by .NET Core relies on the newest Windows CNG APIs. - // Unfortunately, when a new key is generated using the default RSA.Create() method, it is not bound - // to the machine account, which may cause security exceptions when running Orchard on IIS using a - // virtual application pool identity or without the profile loading feature enabled (off by default). - // To ensure a RSA key can be generated flawlessly, it is manually created using the managed CNG APIs. - // For more information, visit https://github.com/openiddict/openiddict-core/issues/204. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // Warning: ensure a null key name is specified to ensure the RSA key is not persisted by CNG. - var key = CngKey.Create(CngAlgorithm.Rsa, keyName: null, new CngKeyCreationParameters - { - ExportPolicy = CngExportPolicies.AllowPlaintextExport, - KeyCreationOptions = CngKeyCreationOptions.MachineKey, - Parameters = { new CngProperty("Length", BitConverter.GetBytes(size), CngPropertyOptions.None) } - }); - - return new RSACng(key); - } + // Warning: ensure a null key name is specified to ensure the RSA key is not persisted by CNG. + var key = CngKey.Create(CngAlgorithm.Rsa, keyName: null, new CngKeyCreationParameters + { + ExportPolicy = CngExportPolicies.AllowPlaintextExport, + KeyCreationOptions = CngKeyCreationOptions.MachineKey, + Parameters = { new CngProperty("Length", BitConverter.GetBytes(size), CngPropertyOptions.None) } + }); - return RSA.Create(size); + return new RSACng(key); } - private async Task PersistCertificateAsync(DirectoryInfo directory, X509Certificate2 certificate) - { - var password = GeneratePassword(); - var path = Path.Combine(directory.FullName, Guid.NewGuid().ToString()); + return RSA.Create(size); + } - await File.WriteAllBytesAsync(Path.ChangeExtension(path, ".pfx"), certificate.Export(X509ContentType.Pfx, password)); - await File.WriteAllTextAsync(Path.ChangeExtension(path, ".pwd"), _dataProtector.Protect(password)); + private async Task PersistCertificateAsync(DirectoryInfo directory, X509Certificate2 certificate) + { + var password = GeneratePassword(); + var path = Path.Combine(directory.FullName, Guid.NewGuid().ToString()); - static string GeneratePassword() - { - Span data = stackalloc byte[256 / 8]; - RandomNumberGenerator.Fill(data); - return Convert.ToBase64String(data, Base64FormattingOptions.None); - } + await File.WriteAllBytesAsync(Path.ChangeExtension(path, ".pfx"), certificate.Export(X509ContentType.Pfx, password)); + await File.WriteAllTextAsync(Path.ChangeExtension(path, ".pwd"), _dataProtector.Protect(password)); + + static string GeneratePassword() + { + Span data = stackalloc byte[256 / 8]; + RandomNumberGenerator.Fill(data); + return Convert.ToBase64String(data, Base64FormattingOptions.None); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdValidationService.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdValidationService.cs index 4a63ac63eb3..f3ead40e48e 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdValidationService.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Services/OpenIdValidationService.cs @@ -14,215 +14,214 @@ using OrchardCore.OpenId.Settings; using OrchardCore.Settings; -namespace OrchardCore.OpenId.Services +namespace OrchardCore.OpenId.Services; + +public class OpenIdValidationService : IOpenIdValidationService { - public class OpenIdValidationService : IOpenIdValidationService + private readonly ShellDescriptor _shellDescriptor; + private readonly ShellSettings _shellSettings; + private readonly IShellHost _shellHost; + private readonly ISiteService _siteService; + + protected readonly IStringLocalizer S; + + public OpenIdValidationService( + ShellDescriptor shellDescriptor, + ShellSettings shellSettings, + IShellHost shellHost, + ISiteService siteService, + IStringLocalizer stringLocalizer) { - private readonly ShellDescriptor _shellDescriptor; - private readonly ShellSettings _shellSettings; - private readonly IShellHost _shellHost; - private readonly ISiteService _siteService; - - protected readonly IStringLocalizer S; - - public OpenIdValidationService( - ShellDescriptor shellDescriptor, - ShellSettings shellSettings, - IShellHost shellHost, - ISiteService siteService, - IStringLocalizer stringLocalizer) - { - _shellDescriptor = shellDescriptor; - _shellSettings = shellSettings; - _shellHost = shellHost; - _siteService = siteService; - S = stringLocalizer; - } + _shellDescriptor = shellDescriptor; + _shellSettings = shellSettings; + _shellHost = shellHost; + _siteService = siteService; + S = stringLocalizer; + } - public async Task GetSettingsAsync() - { - var container = await _siteService.GetSiteSettingsAsync(); - return GetSettingsFromContainer(container); - } + public async Task GetSettingsAsync() + { + var container = await _siteService.GetSiteSettingsAsync(); + return GetSettingsFromContainer(container); + } + + public async Task LoadSettingsAsync() + { + var container = await _siteService.LoadSiteSettingsAsync(); + return GetSettingsFromContainer(container); + } - public async Task LoadSettingsAsync() + private OpenIdValidationSettings GetSettingsFromContainer(ISite container) + { + if (container.Properties.TryGetPropertyValue(nameof(OpenIdValidationSettings), out var settings)) { - var container = await _siteService.LoadSiteSettingsAsync(); - return GetSettingsFromContainer(container); + return settings.ToObject(); } - private OpenIdValidationSettings GetSettingsFromContainer(ISite container) + // If the OpenID validation settings haven't been populated yet, assume the validation + // feature will use the OpenID server registered in this tenant if it's been enabled. + if (_shellDescriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) { - if (container.Properties.TryGetPropertyValue(nameof(OpenIdValidationSettings), out var settings)) + return new OpenIdValidationSettings { - return settings.ToObject(); - } + Tenant = _shellSettings.Name, + }; + } - // If the OpenID validation settings haven't been populated yet, assume the validation - // feature will use the OpenID server registered in this tenant if it's been enabled. - if (_shellDescriptor.Features.Any(feature => feature.Id == OpenIdConstants.Features.Server)) - { - return new OpenIdValidationSettings - { - Tenant = _shellSettings.Name, - }; - } + return new OpenIdValidationSettings(); + } - return new OpenIdValidationSettings(); - } + public async Task UpdateSettingsAsync(OpenIdValidationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task UpdateSettingsAsync(OpenIdValidationSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + var container = await _siteService.LoadSiteSettingsAsync(); + container.Properties[nameof(OpenIdValidationSettings)] = JObject.FromObject(settings); + await _siteService.UpdateSiteSettingsAsync(container); + } - var container = await _siteService.LoadSiteSettingsAsync(); - container.Properties[nameof(OpenIdValidationSettings)] = JObject.FromObject(settings); - await _siteService.UpdateSiteSettingsAsync(container); - } + public async Task> ValidateSettingsAsync(OpenIdValidationSettings settings) + { + ArgumentNullException.ThrowIfNull(settings); - public async Task> ValidateSettingsAsync(OpenIdValidationSettings settings) - { - ArgumentNullException.ThrowIfNull(settings); + var results = ImmutableArray.CreateBuilder(); - var results = ImmutableArray.CreateBuilder(); + if (!(settings.Authority == null ^ string.IsNullOrEmpty(settings.Tenant))) + { + results.Add(new ValidationResult(S["Either a tenant or an authority must be registered."], new[] + { + nameof(settings.Authority), + nameof(settings.Tenant), + })); + } - if (!(settings.Authority == null ^ string.IsNullOrEmpty(settings.Tenant))) + if (settings.Authority != null) + { + if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) { - results.Add(new ValidationResult(S["Either a tenant or an authority must be registered."], new[] + results.Add(new ValidationResult(S["The specified authority is not valid."], new[] { - nameof(settings.Authority), - nameof(settings.Tenant), + nameof(settings.Authority) })); } - if (settings.Authority != null) + if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) { - if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString()) - { - results.Add(new ValidationResult(S["The specified authority is not valid."], new[] - { - nameof(settings.Authority) - })); - } - - if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment)) + results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] { - results.Add(new ValidationResult(S["The authority cannot contain a query string or a fragment."], new[] - { - nameof(settings.Authority), - })); - } - + nameof(settings.Authority), + })); } - if (settings.MetadataAddress != null) - { - if (!settings.MetadataAddress.IsAbsoluteUri || !settings.MetadataAddress.IsWellFormedOriginalString()) - { - results.Add(new ValidationResult(S["The specified metadata address is not valid."], new[] - { - nameof(settings.MetadataAddress), - })); - } - - if (!string.IsNullOrEmpty(settings.MetadataAddress.Query) || !string.IsNullOrEmpty(settings.MetadataAddress.Fragment)) - { - results.Add(new ValidationResult(S["The metadata address cannot contain a query string or a fragment."], new[] - { - nameof(settings.MetadataAddress), - })); - } - - if (!string.IsNullOrEmpty(settings.Tenant)) - { - results.Add(new ValidationResult(S["No metatada address can be set when using another tenant."], new[] - { - nameof(settings.MetadataAddress), - })); - } - } + } - if (!string.IsNullOrEmpty(settings.Tenant) && !string.IsNullOrEmpty(settings.Audience)) + if (settings.MetadataAddress != null) + { + if (!settings.MetadataAddress.IsAbsoluteUri || !settings.MetadataAddress.IsWellFormedOriginalString()) { - results.Add(new ValidationResult(S["No audience can be set when using another tenant."], new[] + results.Add(new ValidationResult(S["The specified metadata address is not valid."], new[] { - nameof(settings.Audience), + nameof(settings.MetadataAddress), })); } - if (settings.Authority != null && string.IsNullOrEmpty(settings.Audience)) + if (!string.IsNullOrEmpty(settings.MetadataAddress.Query) || !string.IsNullOrEmpty(settings.MetadataAddress.Fragment)) { - results.Add(new ValidationResult(S["An audience must be set when configuring the authority."], new[] + results.Add(new ValidationResult(S["The metadata address cannot contain a query string or a fragment."], new[] { - nameof(settings.Audience), + nameof(settings.MetadataAddress), })); } - if (settings.Authority == null && settings.DisableTokenTypeValidation) + if (!string.IsNullOrEmpty(settings.Tenant)) { - results.Add(new ValidationResult(S["Token type validation can only be disabled for remote servers."], new[] + results.Add(new ValidationResult(S["No metatada address can be set when using another tenant."], new[] { - nameof(settings.DisableTokenTypeValidation), + nameof(settings.MetadataAddress), })); } + } - if (!string.IsNullOrEmpty(settings.Audience) && - settings.Audience.StartsWith(OpenIdConstants.Prefixes.Tenant, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(settings.Tenant) && !string.IsNullOrEmpty(settings.Audience)) + { + results.Add(new ValidationResult(S["No audience can be set when using another tenant."], new[] { - results.Add(new ValidationResult(S["The audience cannot start with the special 'oct:' prefix."], new[] - { - nameof(settings.Audience), - })); - } + nameof(settings.Audience), + })); + } - // If a tenant was specified, ensure it is valid, that the OpenID server feature - // was enabled and that at least a scope linked with the current tenant exists. - if (!string.IsNullOrEmpty(settings.Tenant) && - !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) + if (settings.Authority != null && string.IsNullOrEmpty(settings.Audience)) + { + results.Add(new ValidationResult(S["An audience must be set when configuring the authority."], new[] { - if (!_shellHost.TryGetSettings(settings.Tenant, out var shellSettings)) - { - results.Add(new ValidationResult(S["The specified tenant is not valid."])); - } - else - { - var shellScope = await _shellHost.GetScopeAsync(shellSettings); + nameof(settings.Audience), + })); + } + + if (settings.Authority == null && settings.DisableTokenTypeValidation) + { + results.Add(new ValidationResult(S["Token type validation can only be disabled for remote servers."], new[] + { + nameof(settings.DisableTokenTypeValidation), + })); + } + + if (!string.IsNullOrEmpty(settings.Audience) && + settings.Audience.StartsWith(OpenIdConstants.Prefixes.Tenant, StringComparison.OrdinalIgnoreCase)) + { + results.Add(new ValidationResult(S["The audience cannot start with the special 'oct:' prefix."], new[] + { + nameof(settings.Audience), + })); + } - await shellScope.UsingAsync(async scope => + // If a tenant was specified, ensure it is valid, that the OpenID server feature + // was enabled and that at least a scope linked with the current tenant exists. + if (!string.IsNullOrEmpty(settings.Tenant) && + !string.Equals(settings.Tenant, _shellSettings.Name, StringComparison.Ordinal)) + { + if (!_shellHost.TryGetSettings(settings.Tenant, out var shellSettings)) + { + results.Add(new ValidationResult(S["The specified tenant is not valid."])); + } + else + { + var shellScope = await _shellHost.GetScopeAsync(shellSettings); + + await shellScope.UsingAsync(async scope => + { + var options = scope.ServiceProvider.GetRequiredService>().CurrentValue; + if (options.UseReferenceAccessTokens) { - var options = scope.ServiceProvider.GetRequiredService>().CurrentValue; - if (options.UseReferenceAccessTokens) + results.Add(new ValidationResult(S["Selecting a server tenant for which reference access tokens are enabled is currently not supported."], new[] { - results.Add(new ValidationResult(S["Selecting a server tenant for which reference access tokens are enabled is currently not supported."], new[] - { - nameof(settings.Tenant), - })); - } + nameof(settings.Tenant), + })); + } - var manager = scope.ServiceProvider.GetService(); - if (manager == null) + var manager = scope.ServiceProvider.GetService(); + if (manager == null) + { + results.Add(new ValidationResult(S["The specified tenant is not valid."], new[] + { + nameof(settings.Tenant), + })); + } + else + { + var resource = OpenIdConstants.Prefixes.Tenant + _shellSettings.Name; + if (!await manager.FindByResourceAsync(resource).AnyAsync()) { - results.Add(new ValidationResult(S["The specified tenant is not valid."], new[] + results.Add(new ValidationResult(S["No appropriate scope was found."], new[] { nameof(settings.Tenant), })); } - else - { - var resource = OpenIdConstants.Prefixes.Tenant + _shellSettings.Name; - if (!await manager.FindByResourceAsync(resource).AnyAsync()) - { - results.Add(new ValidationResult(S["No appropriate scope was found."], new[] - { - nameof(settings.Tenant), - })); - } - } - }); - } + } + }); } - - return results.ToImmutable(); } + + return results.ToImmutable(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdClientSettings.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdClientSettings.cs index d6d02500e1d..9c399a14558 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdClientSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdClientSettings.cs @@ -1,27 +1,26 @@ using System; using System.Collections.Generic; -namespace OrchardCore.OpenId.Settings +namespace OrchardCore.OpenId.Settings; + +public class OpenIdClientSettings { - public class OpenIdClientSettings - { - public string DisplayName { get; set; } - public Uri Authority { get; set; } - public string ClientId { get; set; } - public string ClientSecret { get; set; } - public string CallbackPath { get; set; } - public string SignedOutRedirectUri { get; set; } - public string SignedOutCallbackPath { get; set; } - public IEnumerable Scopes { get; set; } - public string ResponseType { get; set; } - public string ResponseMode { get; set; } - public bool StoreExternalTokens { get; set; } - public ParameterSetting[] Parameters { get; set; } = []; - } + public string DisplayName { get; set; } + public Uri Authority { get; set; } + public string ClientId { get; set; } + public string ClientSecret { get; set; } + public string CallbackPath { get; set; } + public string SignedOutRedirectUri { get; set; } + public string SignedOutCallbackPath { get; set; } + public IEnumerable Scopes { get; set; } + public string ResponseType { get; set; } + public string ResponseMode { get; set; } + public bool StoreExternalTokens { get; set; } + public ParameterSetting[] Parameters { get; set; } = []; +} - public class ParameterSetting - { - public string Name { get; set; } - public string Value { get; set; } - } +public class ParameterSetting +{ + public string Name { get; set; } + public string Value { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdServerSettings.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdServerSettings.cs index 10d63b6a7ed..f87bf2a3c3c 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdServerSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdServerSettings.cs @@ -2,50 +2,49 @@ using System.Security.Cryptography.X509Certificates; using Microsoft.AspNetCore.Http; -namespace OrchardCore.OpenId.Settings +namespace OrchardCore.OpenId.Settings; + +public class OpenIdServerSettings { - public class OpenIdServerSettings - { - public TokenFormat AccessTokenFormat { get; set; } + public TokenFormat AccessTokenFormat { get; set; } - public Uri Authority { get; set; } + public Uri Authority { get; set; } - public bool DisableAccessTokenEncryption { get; set; } - public StoreLocation? EncryptionCertificateStoreLocation { get; set; } - public StoreName? EncryptionCertificateStoreName { get; set; } - public string EncryptionCertificateThumbprint { get; set; } - public StoreLocation? SigningCertificateStoreLocation { get; set; } - public StoreName? SigningCertificateStoreName { get; set; } - public string SigningCertificateThumbprint { get; set; } - public PathString AuthorizationEndpointPath { get; set; } + public bool DisableAccessTokenEncryption { get; set; } + public StoreLocation? EncryptionCertificateStoreLocation { get; set; } + public StoreName? EncryptionCertificateStoreName { get; set; } + public string EncryptionCertificateThumbprint { get; set; } + public StoreLocation? SigningCertificateStoreLocation { get; set; } + public StoreName? SigningCertificateStoreName { get; set; } + public string SigningCertificateThumbprint { get; set; } + public PathString AuthorizationEndpointPath { get; set; } - public PathString LogoutEndpointPath { get; set; } + public PathString LogoutEndpointPath { get; set; } - public PathString TokenEndpointPath { get; set; } + public PathString TokenEndpointPath { get; set; } - public PathString UserinfoEndpointPath { get; set; } + public PathString UserinfoEndpointPath { get; set; } - public PathString IntrospectionEndpointPath { get; set; } + public PathString IntrospectionEndpointPath { get; set; } - public PathString RevocationEndpointPath { get; set; } + public PathString RevocationEndpointPath { get; set; } - public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } + public bool AllowPasswordFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } + public bool AllowHybridFlow { get; set; } + public bool AllowImplicitFlow { get; set; } - public bool DisableRollingRefreshTokens { get; set; } + public bool DisableRollingRefreshTokens { get; set; } - public bool UseReferenceAccessTokens { get; set; } + public bool UseReferenceAccessTokens { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public enum TokenFormat - { - DataProtection = 0, - JsonWebToken = 1 - } + public enum TokenFormat + { + DataProtection = 0, + JsonWebToken = 1 } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdValidationSettings.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdValidationSettings.cs index 873bd399c00..8b77f571a99 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdValidationSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Settings/OpenIdValidationSettings.cs @@ -1,13 +1,12 @@ using System; -namespace OrchardCore.OpenId.Settings +namespace OrchardCore.OpenId.Settings; + +public class OpenIdValidationSettings { - public class OpenIdValidationSettings - { - public string Audience { get; set; } - public Uri Authority { get; set; } - public bool DisableTokenTypeValidation { get; set; } - public string Tenant { get; set; } - public Uri MetadataAddress { get; set; } - } + public string Audience { get; set; } + public Uri Authority { get; set; } + public bool DisableTokenTypeValidation { get; set; } + public string Tenant { get; set; } + public Uri MetadataAddress { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Startup.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Startup.cs index 392fc2808bb..7fd227b4e45 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Startup.cs @@ -35,245 +35,244 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.OpenId +namespace OrchardCore.OpenId; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - // Register the OpenIddict core services and the Orchard migrations, managers and default YesSql stores. - // The default YesSql stores can be replaced by another database by referencing the corresponding - // OpenIddict package (e.g OpenIddict.EntityFrameworkCore) and registering it in the options. - services.AddOpenIddict() - .AddCore(options => - { - options.AddOrchardMigrations() - .UseOrchardManagers() - .UseYesSql(); - }); - - // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. - services.TryAddEnumerable(new[] + // Register the OpenIddict core services and the Orchard migrations, managers and default YesSql stores. + // The default YesSql stores can be replaced by another database by referencing the corresponding + // OpenIddict package (e.g OpenIddict.EntityFrameworkCore) and registering it in the options. + services.AddOpenIddict() + .AddCore(options => { - ServiceDescriptor.Scoped(), - ServiceDescriptor.Scoped(), + options.AddOrchardMigrations() + .UseOrchardManagers() + .UseYesSql(); }); - } + + // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. + services.TryAddEnumerable(new[] + { + ServiceDescriptor.Scoped(), + ServiceDescriptor.Scoped(), + }); } +} - [Feature(OpenIdConstants.Features.Client)] - public sealed class ClientStartup : StartupBase +[Feature(OpenIdConstants.Features.Client)] +public sealed class ClientStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.TryAddSingleton(); + services.TryAddSingleton(); - // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. - services.TryAddEnumerable(new[] - { - ServiceDescriptor.Scoped, OpenIdClientSettingsDisplayDriver>(), - ServiceDescriptor.Scoped() - }); + // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. + services.TryAddEnumerable(new[] + { + ServiceDescriptor.Scoped, OpenIdClientSettingsDisplayDriver>(), + ServiceDescriptor.Scoped() + }); - // Register the options initializers required by the OpenID Connect client handler. - services.TryAddEnumerable(new[] - { - // Orchard-specific initializers: - ServiceDescriptor.Singleton, OpenIdClientConfiguration>(), - ServiceDescriptor.Singleton, OpenIdClientConfiguration>(), + // Register the options initializers required by the OpenID Connect client handler. + services.TryAddEnumerable(new[] + { + // Orchard-specific initializers: + ServiceDescriptor.Singleton, OpenIdClientConfiguration>(), + ServiceDescriptor.Singleton, OpenIdClientConfiguration>(), - // Built-in initializers: - ServiceDescriptor.Singleton, OpenIdConnectPostConfigureOptions>() - }); - } + // Built-in initializers: + ServiceDescriptor.Singleton, OpenIdConnectPostConfigureOptions>() + }); } +} - [Feature(OpenIdConstants.Features.Server)] - public sealed class ServerStartup : StartupBase +[Feature(OpenIdConstants.Features.Server)] +public sealed class ServerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.AddOpenIddict() + .AddServer(options => + { + options.UseAspNetCore(); + options.UseDataProtection(); + }); + + services.TryAddSingleton(); + + // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. + services.TryAddEnumerable(new[] { - services.AddOpenIddict() - .AddServer(options => - { - options.UseAspNetCore(); - options.UseDataProtection(); - }); + ServiceDescriptor.Scoped(), + ServiceDescriptor.Scoped, OpenIdServerSettingsDisplayDriver>(), + ServiceDescriptor.Scoped(), + ServiceDescriptor.Scoped(), + ServiceDescriptor.Scoped(), - services.TryAddSingleton(); + ServiceDescriptor.Singleton() + }); - // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. - services.TryAddEnumerable(new[] - { - ServiceDescriptor.Scoped(), - ServiceDescriptor.Scoped, OpenIdServerSettingsDisplayDriver>(), - ServiceDescriptor.Scoped(), - ServiceDescriptor.Scoped(), - ServiceDescriptor.Scoped(), + // Note: the OpenIddict ASP.NET host adds an authentication options initializer that takes care of + // registering the server ASP.NET Core handler. Yet, it MUST NOT be registered at this stage + // as it is lazily registered by OpenIdServerConfiguration only after checking the OpenID server + // settings are valid and can be safely used in this tenant without causing runtime exceptions. + // To prevent that, the initializer is manually removed from the services collection of the tenant. + services.RemoveAll, OpenIddictServerAspNetCoreConfiguration>(); - ServiceDescriptor.Singleton() - }); + services.TryAddEnumerable(new[] + { + ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), + ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), + ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), + ServiceDescriptor.Singleton, OpenIdServerConfiguration>() + }); + } - // Note: the OpenIddict ASP.NET host adds an authentication options initializer that takes care of - // registering the server ASP.NET Core handler. Yet, it MUST NOT be registered at this stage - // as it is lazily registered by OpenIdServerConfiguration only after checking the OpenID server - // settings are valid and can be safely used in this tenant without causing runtime exceptions. - // To prevent that, the initializer is manually removed from the services collection of the tenant. - services.RemoveAll, OpenIddictServerAspNetCoreConfiguration>(); + public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var settings = await GetServerSettingsAsync(); + if (settings == null) + { + return; + } - services.TryAddEnumerable(new[] - { - ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), - ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), - ServiceDescriptor.Singleton, OpenIdServerConfiguration>(), - ServiceDescriptor.Singleton, OpenIdServerConfiguration>() - }); + if (settings.AuthorizationEndpointPath.HasValue) + { + routes.MapAreaControllerRoute( + name: "Access.Authorize", + areaName: typeof(Startup).Namespace, + pattern: settings.AuthorizationEndpointPath.Value, + defaults: new { controller = "Access", action = "Authorize" } + ); } - public override async ValueTask ConfigureAsync(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + if (settings.LogoutEndpointPath.HasValue) { - var settings = await GetServerSettingsAsync(); - if (settings == null) - { - return; - } + routes.MapAreaControllerRoute( + name: "Access.Logout", + areaName: typeof(Startup).Namespace, + pattern: settings.LogoutEndpointPath.Value, + defaults: new { controller = "Access", action = "Logout" } + ); + } - if (settings.AuthorizationEndpointPath.HasValue) - { - routes.MapAreaControllerRoute( - name: "Access.Authorize", - areaName: typeof(Startup).Namespace, - pattern: settings.AuthorizationEndpointPath.Value, - defaults: new { controller = "Access", action = "Authorize" } - ); - } + if (settings.TokenEndpointPath.HasValue) + { + routes.MapAreaControllerRoute( + name: "Access.Token", + areaName: typeof(Startup).Namespace, + pattern: settings.TokenEndpointPath.Value, + defaults: new { controller = "Access", action = "Token" } + ); + } - if (settings.LogoutEndpointPath.HasValue) - { - routes.MapAreaControllerRoute( - name: "Access.Logout", - areaName: typeof(Startup).Namespace, - pattern: settings.LogoutEndpointPath.Value, - defaults: new { controller = "Access", action = "Logout" } - ); - } + if (settings.UserinfoEndpointPath.HasValue) + { + routes.MapAreaControllerRoute( + name: "UserInfo.Me", + areaName: typeof(Startup).Namespace, + pattern: settings.UserinfoEndpointPath.Value, + defaults: new { controller = "UserInfo", action = "Me" } + ); + } - if (settings.TokenEndpointPath.HasValue) - { - routes.MapAreaControllerRoute( - name: "Access.Token", - areaName: typeof(Startup).Namespace, - pattern: settings.TokenEndpointPath.Value, - defaults: new { controller = "Access", action = "Token" } - ); - } + async Task GetServerSettingsAsync() + { + // Note: the OpenID server service is registered as a singleton service and thus can be + // safely used with the non-scoped/root service provider available at this stage. + var service = serviceProvider.GetRequiredService(); - if (settings.UserinfoEndpointPath.HasValue) + var configuration = await service.GetSettingsAsync(); + if ((await service.ValidateSettingsAsync(configuration)).Any(result => result != ValidationResult.Success)) { - routes.MapAreaControllerRoute( - name: "UserInfo.Me", - areaName: typeof(Startup).Namespace, - pattern: settings.UserinfoEndpointPath.Value, - defaults: new { controller = "UserInfo", action = "Me" } - ); + return null; } - async Task GetServerSettingsAsync() - { - // Note: the OpenID server service is registered as a singleton service and thus can be - // safely used with the non-scoped/root service provider available at this stage. - var service = serviceProvider.GetRequiredService(); - - var configuration = await service.GetSettingsAsync(); - if ((await service.ValidateSettingsAsync(configuration)).Any(result => result != ValidationResult.Success)) - { - return null; - } - - return configuration; - } + return configuration; } } +} - [RequireFeatures("OrchardCore.Deployment", OpenIdConstants.Features.Server)] - public sealed class ServerDeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment", OpenIdConstants.Features.Server)] +public sealed class ServerDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } +} - [Feature(OpenIdConstants.Features.Validation)] - public sealed class ValidationStartup : StartupBase +[Feature(OpenIdConstants.Features.Validation)] +public sealed class ValidationStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddOpenIddict() - .AddValidation(options => - { - options.UseAspNetCore(); - options.UseDataProtection(); - options.UseSystemNetHttp(); - }); - - services.TryAddSingleton(); - - // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. - services.TryAddEnumerable(new[] + services.AddOpenIddict() + .AddValidation(options => { - ServiceDescriptor.Scoped, OpenIdValidationSettingsDisplayDriver>(), - ServiceDescriptor.Scoped() + options.UseAspNetCore(); + options.UseDataProtection(); + options.UseSystemNetHttp(); }); - // Note: the OpenIddict ASP.NET host adds an authentication options initializer that takes care of - // registering the validation handler. Yet, it MUST NOT be registered at this stage as it is - // lazily registered by OpenIdValidationConfiguration only after checking the OpenID validation - // settings are valid and can be safely used in this tenant without causing runtime exceptions. - // To prevent that, the initializer is manually removed from the services collection of the tenant. - services.RemoveAll, OpenIddictValidationAspNetCoreConfiguration>(); + services.TryAddSingleton(); - services.TryAddEnumerable(new[] - { - ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), - ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), - ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), - ServiceDescriptor.Singleton, OpenIdValidationConfiguration>() - }); - } + // Note: the following services are registered using TryAddEnumerable to prevent duplicate registrations. + services.TryAddEnumerable(new[] + { + ServiceDescriptor.Scoped, OpenIdValidationSettingsDisplayDriver>(), + ServiceDescriptor.Scoped() + }); + + // Note: the OpenIddict ASP.NET host adds an authentication options initializer that takes care of + // registering the validation handler. Yet, it MUST NOT be registered at this stage as it is + // lazily registered by OpenIdValidationConfiguration only after checking the OpenID validation + // settings are valid and can be safely used in this tenant without causing runtime exceptions. + // To prevent that, the initializer is manually removed from the services collection of the tenant. + services.RemoveAll, OpenIddictValidationAspNetCoreConfiguration>(); + + services.TryAddEnumerable(new[] + { + ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), + ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), + ServiceDescriptor.Singleton, OpenIdValidationConfiguration>(), + ServiceDescriptor.Singleton, OpenIdValidationConfiguration>() + }); } +} - [RequireFeatures("OrchardCore.Deployment", OpenIdConstants.Features.Validation)] - public sealed class ValidationDeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment", OpenIdConstants.Features.Validation)] +public sealed class ValidationDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } +} - internal static class OpenIdServiceCollectionExtensions +internal static class OpenIdServiceCollectionExtensions +{ + public static IServiceCollection RemoveAll(this IServiceCollection services, Type serviceType, Type implementationType) { - public static IServiceCollection RemoveAll(this IServiceCollection services, Type serviceType, Type implementationType) - { - Debug.Assert(services != null, "The services collection shouldn't be null."); - Debug.Assert(serviceType != null, "The service type shouldn't be null."); - Debug.Assert(implementationType != null, "The implementation type shouldn't be null."); + Debug.Assert(services != null, "The services collection shouldn't be null."); + Debug.Assert(serviceType != null, "The service type shouldn't be null."); + Debug.Assert(implementationType != null, "The implementation type shouldn't be null."); - for (var index = services.Count - 1; index >= 0; index--) + for (var index = services.Count - 1; index >= 0; index--) + { + var descriptor = services[index]; + if (descriptor.ServiceType == serviceType && descriptor.GetImplementationType() == implementationType) { - var descriptor = services[index]; - if (descriptor.ServiceType == serviceType && descriptor.GetImplementationType() == implementationType) - { - services.RemoveAt(index); - } + services.RemoveAt(index); } - - return services; } - public static IServiceCollection RemoveAll(this IServiceCollection services) - where TImplementation : TService - => services.RemoveAll(typeof(TService), typeof(TImplementation)); + return services; } + + public static IServiceCollection RemoveAll(this IServiceCollection services) + where TImplementation : TService + => services.RemoveAll(typeof(TService), typeof(TImplementation)); } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/Tasks/OpenIdBackgroundTask.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/Tasks/OpenIdBackgroundTask.cs index 826c530a217..52f8acbc829 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/Tasks/OpenIdBackgroundTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/Tasks/OpenIdBackgroundTask.cs @@ -8,85 +8,84 @@ using OrchardCore.OpenId.Abstractions.Managers; using OrchardCore.OpenId.Services; -namespace OrchardCore.OpenId.Tasks +namespace OrchardCore.OpenId.Tasks; + +[BackgroundTask( + Title = "OpenID Cleaner", + Schedule = "*/30 * * * *", + Description = "Performs various cleanup operations for OpenID features.")] +public sealed class OpenIdBackgroundTask : IBackgroundTask { - [BackgroundTask( - Title = "OpenID Cleaner", - Schedule = "*/30 * * * *", - Description = "Performs various cleanup operations for OpenID features.")] - public sealed class OpenIdBackgroundTask : IBackgroundTask + private readonly ILogger _logger; + private readonly IOpenIdServerService _serverService; + + public OpenIdBackgroundTask( + ILogger logger, + IOpenIdServerService serverService) { - private readonly ILogger _logger; - private readonly IOpenIdServerService _serverService; + _logger = logger; + _serverService = serverService; + } - public OpenIdBackgroundTask( - ILogger logger, - IOpenIdServerService serverService) + public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + try { - _logger = logger; - _serverService = serverService; + await _serverService.PruneManagedCertificatesAsync(); } - - public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) + { + _logger.LogDebug("The X.509 certificates pruning task was aborted."); + } + catch (Exception exception) { - try - { - await _serverService.PruneManagedCertificatesAsync(); - } - catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) - { - _logger.LogDebug("The X.509 certificates pruning task was aborted."); - } - catch (Exception exception) - { - _logger.LogError(exception, "An error occurred while pruning X.509 certificates from the shell storage."); - } + _logger.LogError(exception, "An error occurred while pruning X.509 certificates from the shell storage."); + } - // Note: this background task is responsible of automatically removing orphaned tokens/authorizations - // (i.e tokens that are no longer valid and ad-hoc authorizations that have no valid tokens associated). - // Import: since tokens associated to ad-hoc authorizations are not removed as part of the same operation, - // the tokens MUST be deleted before removing the ad-hoc authorizations that no longer have any token. + // Note: this background task is responsible of automatically removing orphaned tokens/authorizations + // (i.e tokens that are no longer valid and ad-hoc authorizations that have no valid tokens associated). + // Import: since tokens associated to ad-hoc authorizations are not removed as part of the same operation, + // the tokens MUST be deleted before removing the ad-hoc authorizations that no longer have any token. - // Note: the authorization/token managers MUST be resolved from the scoped provider - // as they depend on scoped stores that should be disposed as soon as possible. + // Note: the authorization/token managers MUST be resolved from the scoped provider + // as they depend on scoped stores that should be disposed as soon as possible. - // Note: this task uses a hardcoded minimum token/authorization lifespan of 14 days, - // which matches the default value used by the OpenIddict Quartz.NET integration. - var threshold = DateTimeOffset.UtcNow - TimeSpan.FromDays(14); + // Note: this task uses a hardcoded minimum token/authorization lifespan of 14 days, + // which matches the default value used by the OpenIddict Quartz.NET integration. + var threshold = DateTimeOffset.UtcNow - TimeSpan.FromDays(14); - try - { - await serviceProvider.GetRequiredService().PruneAsync(threshold, cancellationToken); - } - catch (OpenIddictExceptions.ConcurrencyException exception) - { - _logger.LogDebug(exception, "A concurrency error occurred while pruning tokens from the database."); - } - catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) - { - _logger.LogDebug("The tokens pruning task was aborted."); - } - catch (Exception exception) - { - _logger.LogError(exception, "An error occurred while pruning tokens from the database."); - } + try + { + await serviceProvider.GetRequiredService().PruneAsync(threshold, cancellationToken); + } + catch (OpenIddictExceptions.ConcurrencyException exception) + { + _logger.LogDebug(exception, "A concurrency error occurred while pruning tokens from the database."); + } + catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) + { + _logger.LogDebug("The tokens pruning task was aborted."); + } + catch (Exception exception) + { + _logger.LogError(exception, "An error occurred while pruning tokens from the database."); + } - try - { - await serviceProvider.GetRequiredService().PruneAsync(threshold, cancellationToken); - } - catch (OpenIddictExceptions.ConcurrencyException exception) - { - _logger.LogDebug(exception, "A concurrency error occurred while pruning authorizations from the database."); - } - catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) - { - _logger.LogDebug("The authorizations pruning task was aborted."); - } - catch (Exception exception) - { - _logger.LogError(exception, "An error occurred while pruning authorizations from the database."); - } + try + { + await serviceProvider.GetRequiredService().PruneAsync(threshold, cancellationToken); + } + catch (OpenIddictExceptions.ConcurrencyException exception) + { + _logger.LogDebug(exception, "A concurrency error occurred while pruning authorizations from the database."); + } + catch (OperationCanceledException exception) when (exception.CancellationToken == cancellationToken) + { + _logger.LogDebug("The authorizations pruning task was aborted."); + } + catch (Exception exception) + { + _logger.LogError(exception, "An error occurred while pruning authorizations from the database."); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/AuthorizeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/AuthorizeViewModel.cs index 52d56dee49d..94daf0fe2cd 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/AuthorizeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/AuthorizeViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class AuthorizeViewModel { - public class AuthorizeViewModel - { - public string ApplicationName { get; set; } + public string ApplicationName { get; set; } - public string RequestId { get; set; } + public string RequestId { get; set; } - public string Scope { get; set; } - } + public string Scope { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdApplicationViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdApplicationViewModel.cs index c8b9d6218eb..7fbb3959261 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdApplicationViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdApplicationViewModel.cs @@ -1,61 +1,60 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class CreateOpenIdApplicationViewModel { - public class CreateOpenIdApplicationViewModel - { - [Required] - public string ClientId { get; set; } + [Required] + public string ClientId { get; set; } - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - [Url(ErrorMessage = "{0} is not well-formed")] - public string RedirectUris { get; set; } + [Url(ErrorMessage = "{0} is not well-formed")] + public string RedirectUris { get; set; } - [Url(ErrorMessage = "{0} is not well-formed")] - public string PostLogoutRedirectUris { get; set; } + [Url(ErrorMessage = "{0} is not well-formed")] + public string PostLogoutRedirectUris { get; set; } - public string Type { get; set; } + public string Type { get; set; } - public string ConsentType { get; set; } + public string ConsentType { get; set; } - public string ClientSecret { get; set; } + public string ClientSecret { get; set; } - public List RoleEntries { get; } = []; + public List RoleEntries { get; } = []; - public List ScopeEntries { get; } = []; + public List ScopeEntries { get; } = []; - public bool AllowPasswordFlow { get; set; } + public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } + public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } + public bool AllowImplicitFlow { get; set; } - public bool AllowLogoutEndpoint { get; set; } + public bool AllowLogoutEndpoint { get; set; } - public bool AllowIntrospectionEndpoint { get; set; } + public bool AllowIntrospectionEndpoint { get; set; } - public bool AllowRevocationEndpoint { get; set; } + public bool AllowRevocationEndpoint { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public class RoleEntry - { - public string Name { get; set; } - public bool Selected { get; set; } - } - public class ScopeEntry - { - public string Name { get; set; } - public bool Selected { get; set; } - } + public class RoleEntry + { + public string Name { get; set; } + public bool Selected { get; set; } + } + public class ScopeEntry + { + public string Name { get; set; } + public bool Selected { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdScopeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdScopeViewModel.cs index df91516a6fa..343ac8130b1 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdScopeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/CreateOpenIdScopeViewModel.cs @@ -1,27 +1,26 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class CreateOpenIdScopeViewModel { - public class CreateOpenIdScopeViewModel - { - public string Description { get; set; } + public string Description { get; set; } - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - [Required] - public string Name { get; set; } + [Required] + public string Name { get; set; } - public string Resources { get; set; } + public string Resources { get; set; } - public List Tenants { get; } = []; + public List Tenants { get; } = []; - public class TenantEntry - { - public bool Current { get; set; } - public string Name { get; set; } - public bool Selected { get; set; } - } + public class TenantEntry + { + public bool Current { get; set; } + public string Name { get; set; } + public bool Selected { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdApplicationViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdApplicationViewModel.cs index f7a71099bdd..2dd85cfdbdb 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdApplicationViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdApplicationViewModel.cs @@ -2,65 +2,64 @@ using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class EditOpenIdApplicationViewModel { - public class EditOpenIdApplicationViewModel - { - [HiddenInput] - public string Id { get; set; } + [HiddenInput] + public string Id { get; set; } - [Required] - public string ClientId { get; set; } + [Required] + public string ClientId { get; set; } - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - [Url(ErrorMessage = "{0} is not well-formed")] - public string RedirectUris { get; set; } + [Url(ErrorMessage = "{0} is not well-formed")] + public string RedirectUris { get; set; } - [Url(ErrorMessage = "{0} is not well-formed")] - public string PostLogoutRedirectUris { get; set; } + [Url(ErrorMessage = "{0} is not well-formed")] + public string PostLogoutRedirectUris { get; set; } - public string Type { get; set; } + public string Type { get; set; } - public string ConsentType { get; set; } + public string ConsentType { get; set; } - public string ClientSecret { get; set; } + public string ClientSecret { get; set; } - public List RoleEntries { get; } = []; + public List RoleEntries { get; } = []; - public List ScopeEntries { get; } = []; + public List ScopeEntries { get; } = []; - public bool AllowPasswordFlow { get; set; } + public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } + public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } + public bool AllowImplicitFlow { get; set; } - public bool AllowLogoutEndpoint { get; set; } + public bool AllowLogoutEndpoint { get; set; } - public bool AllowIntrospectionEndpoint { get; set; } + public bool AllowIntrospectionEndpoint { get; set; } - public bool AllowRevocationEndpoint { get; set; } + public bool AllowRevocationEndpoint { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public class RoleEntry - { - public string Name { get; set; } - public bool Selected { get; set; } - } + public class RoleEntry + { + public string Name { get; set; } + public bool Selected { get; set; } + } - public class ScopeEntry - { - public string Name { get; set; } - public bool Selected { get; set; } - } + public class ScopeEntry + { + public string Name { get; set; } + public bool Selected { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdScopeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdScopeViewModel.cs index 69aad5944a2..251bae17efd 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdScopeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/EditOpenIdScopeViewModel.cs @@ -1,30 +1,29 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class EditOpenIdScopeViewModel { - public class EditOpenIdScopeViewModel - { - public string Description { get; set; } + public string Description { get; set; } - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - [Required] - public string Id { get; set; } + [Required] + public string Id { get; set; } - [Required] - public string Name { get; set; } + [Required] + public string Name { get; set; } - public string Resources { get; set; } + public string Resources { get; set; } - public List Tenants { get; } = []; + public List Tenants { get; } = []; - public class TenantEntry - { - public bool Current { get; set; } - public string Name { get; set; } - public bool Selected { get; set; } - } + public class TenantEntry + { + public bool Current { get; set; } + public string Name { get; set; } + public bool Selected { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/ErrorViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/ErrorViewModel.cs index 9181bc08cb8..6a358012659 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/ErrorViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/ErrorViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class ErrorViewModel { - public class ErrorViewModel - { - public string Error { get; set; } + public string Error { get; set; } - public string ErrorDescription { get; set; } - } + public string ErrorDescription { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/LogoutViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/LogoutViewModel.cs index 21d66c6c609..6a30919e9b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/LogoutViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/LogoutViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class LogoutViewModel { - public class LogoutViewModel - { - public string RequestId { get; set; } - } + public string RequestId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdApplicationsIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdApplicationsIndexViewModel.cs index 14b74fb0cf4..4e69e90eb3c 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdApplicationsIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdApplicationsIndexViewModel.cs @@ -1,21 +1,20 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdApplicationsIndexViewModel { - public class OpenIdApplicationsIndexViewModel - { - [BindNever] - public IList Applications { get; set; } + [BindNever] + public IList Applications { get; set; } - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } +} - public class OpenIdApplicationEntry - { - public string DisplayName { get; set; } - public string Id { get; set; } - public bool IsChecked { get; set; } - } +public class OpenIdApplicationEntry +{ + public string DisplayName { get; set; } + public string Id { get; set; } + public bool IsChecked { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdClientSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdClientSettingsViewModel.cs index d23510b4da4..ad4255ed0ed 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdClientSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdClientSettingsViewModel.cs @@ -1,40 +1,39 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdClientSettingsViewModel { - public class OpenIdClientSettingsViewModel - { - [Required] - public string DisplayName { get; set; } + [Required] + public string DisplayName { get; set; } - public bool TestingModeEnabled { get; set; } + public bool TestingModeEnabled { get; set; } - [Required(ErrorMessage = "Authority is required")] - public string Authority { get; set; } + [Required(ErrorMessage = "Authority is required")] + public string Authority { get; set; } - [Required(ErrorMessage = "ClientId is required")] - public string ClientId { get; set; } + [Required(ErrorMessage = "ClientId is required")] + public string ClientId { get; set; } - public string ClientSecret { get; set; } + public string ClientSecret { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string CallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string CallbackPath { get; set; } - [Url(ErrorMessage = "Invalid SignedOut redirect url")] - public string SignedOutRedirectUri { get; set; } + [Url(ErrorMessage = "Invalid SignedOut redirect url")] + public string SignedOutRedirectUri { get; set; } - [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] - public string SignedOutCallbackPath { get; set; } + [RegularExpression(@"\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]", ErrorMessage = "Invalid path")] + public string SignedOutCallbackPath { get; set; } - public string Scopes { get; set; } - public string ResponseMode { get; set; } - public bool StoreExternalTokens { get; set; } - public bool UseCodeFlow { get; set; } - public bool UseCodeIdTokenTokenFlow { get; set; } - public bool UseCodeIdTokenFlow { get; set; } - public bool UseCodeTokenFlow { get; set; } - public bool UseIdTokenFlow { get; set; } - public bool UseIdTokenTokenFlow { get; set; } - public string Parameters { get; set; } - } + public string Scopes { get; set; } + public string ResponseMode { get; set; } + public bool StoreExternalTokens { get; set; } + public bool UseCodeFlow { get; set; } + public bool UseCodeIdTokenTokenFlow { get; set; } + public bool UseCodeIdTokenFlow { get; set; } + public bool UseCodeTokenFlow { get; set; } + public bool UseIdTokenFlow { get; set; } + public bool UseIdTokenTokenFlow { get; set; } + public string Parameters { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeIndexViewModel.cs index 2f078b947dd..e700d18eb31 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeIndexViewModel.cs @@ -1,19 +1,18 @@ using System.Collections.Generic; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdScopeIndexViewModel { - public class OpenIdScopeIndexViewModel - { - public IList Scopes { get; } = []; - public dynamic Pager { get; set; } - } + public IList Scopes { get; } = []; + public dynamic Pager { get; set; } +} - public class OpenIdScopeEntry - { - public string Description { get; set; } - public string DisplayName { get; set; } - public string Id { get; set; } - public bool IsChecked { get; set; } - public string Name { get; set; } - } +public class OpenIdScopeEntry +{ + public string Description { get; set; } + public string DisplayName { get; set; } + public string Id { get; set; } + public bool IsChecked { get; set; } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeStepViewModel.cs index 6733bf40e50..713082ed8a4 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdScopeStepViewModel.cs @@ -1,13 +1,12 @@ -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdScopeStepViewModel { - public class OpenIdScopeStepViewModel - { - public string Description { get; set; } + public string Description { get; set; } - public string DisplayName { get; set; } + public string DisplayName { get; set; } - public string ScopeName { get; set; } + public string ScopeName { get; set; } - public string Resources { get; set; } - } + public string Resources { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdServerSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdServerSettingsViewModel.cs index 01ef1da703b..7e73b48ee86 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdServerSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdServerSettingsViewModel.cs @@ -3,50 +3,49 @@ using System.Security.Cryptography.X509Certificates; using static OrchardCore.OpenId.Settings.OpenIdServerSettings; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdServerSettingsViewModel { - public class OpenIdServerSettingsViewModel - { - public TokenFormat AccessTokenFormat { get; set; } + public TokenFormat AccessTokenFormat { get; set; } - [Url] - public string Authority { get; set; } - public bool DisableAccessTokenEncryption { get; set; } - public StoreLocation? EncryptionCertificateStoreLocation { get; set; } - public StoreName? EncryptionCertificateStoreName { get; set; } - public string EncryptionCertificateThumbprint { get; set; } - public StoreLocation? SigningCertificateStoreLocation { get; set; } - public StoreName? SigningCertificateStoreName { get; set; } - public string SigningCertificateThumbprint { get; set; } - public IList AvailableCertificates { get; } = []; - public bool EnableTokenEndpoint { get; set; } - public bool EnableAuthorizationEndpoint { get; set; } - public bool EnableLogoutEndpoint { get; set; } - public bool EnableUserInfoEndpoint { get; set; } - public bool EnableIntrospectionEndpoint { get; set; } - public bool EnableRevocationEndpoint { get; set; } - public bool AllowPasswordFlow { get; set; } - public bool AllowClientCredentialsFlow { get; set; } - public bool AllowAuthorizationCodeFlow { get; set; } - public bool AllowRefreshTokenFlow { get; set; } - public bool AllowHybridFlow { get; set; } - public bool AllowImplicitFlow { get; set; } - public bool DisableRollingRefreshTokens { get; set; } - public bool UseReferenceAccessTokens { get; set; } - public bool RequireProofKeyForCodeExchange { get; set; } + [Url] + public string Authority { get; set; } + public bool DisableAccessTokenEncryption { get; set; } + public StoreLocation? EncryptionCertificateStoreLocation { get; set; } + public StoreName? EncryptionCertificateStoreName { get; set; } + public string EncryptionCertificateThumbprint { get; set; } + public StoreLocation? SigningCertificateStoreLocation { get; set; } + public StoreName? SigningCertificateStoreName { get; set; } + public string SigningCertificateThumbprint { get; set; } + public IList AvailableCertificates { get; } = []; + public bool EnableTokenEndpoint { get; set; } + public bool EnableAuthorizationEndpoint { get; set; } + public bool EnableLogoutEndpoint { get; set; } + public bool EnableUserInfoEndpoint { get; set; } + public bool EnableIntrospectionEndpoint { get; set; } + public bool EnableRevocationEndpoint { get; set; } + public bool AllowPasswordFlow { get; set; } + public bool AllowClientCredentialsFlow { get; set; } + public bool AllowAuthorizationCodeFlow { get; set; } + public bool AllowRefreshTokenFlow { get; set; } + public bool AllowHybridFlow { get; set; } + public bool AllowImplicitFlow { get; set; } + public bool DisableRollingRefreshTokens { get; set; } + public bool UseReferenceAccessTokens { get; set; } + public bool RequireProofKeyForCodeExchange { get; set; } - public class CertificateInfo - { - public string FriendlyName { get; set; } - public string Issuer { get; set; } - public DateTime NotAfter { get; set; } - public DateTime NotBefore { get; set; } - public StoreLocation StoreLocation { get; set; } - public StoreName StoreName { get; set; } - public string Subject { get; set; } - public string ThumbPrint { get; set; } - public bool HasPrivateKey { get; set; } - public bool Archived { get; set; } - } + public class CertificateInfo + { + public string FriendlyName { get; set; } + public string Issuer { get; set; } + public DateTime NotAfter { get; set; } + public DateTime NotBefore { get; set; } + public StoreLocation StoreLocation { get; set; } + public StoreName StoreName { get; set; } + public string Subject { get; set; } + public string ThumbPrint { get; set; } + public bool HasPrivateKey { get; set; } + public bool Archived { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdValidationSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdValidationSettingsViewModel.cs index 4681020715d..e9bf706cc7c 100644 --- a/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdValidationSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.OpenId/ViewModels/OpenIdValidationSettingsViewModel.cs @@ -1,18 +1,17 @@ using System.Collections.Generic; -namespace OrchardCore.OpenId.ViewModels +namespace OrchardCore.OpenId.ViewModels; + +public class OpenIdValidationSettingsViewModel { - public class OpenIdValidationSettingsViewModel - { - [Url] - public string MetadataAddress { get; set; } + [Url] + public string MetadataAddress { get; set; } - [Url] - public string Authority { get; set; } + [Url] + public string Authority { get; set; } - public string Audience { get; set; } - public bool DisableTokenTypeValidation { get; set; } - public string Tenant { get; set; } - public IEnumerable AvailableTenants { get; set; } - } + public string Audience { get; set; } + public bool DisableTokenTypeValidation { get; set; } + public string Tenant { get; set; } + public IEnumerable AvailableTenants { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Placements/AdminMenu.cs index c86701ba31d..988cdbce6a2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/AdminMenu.cs @@ -2,34 +2,33 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Placements +namespace OrchardCore.Placements; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Design"], design => design - .Add(S["Placements"], S["Placements"].PrefixPosition(), import => import - .Action("Index", "Admin", "OrchardCore.Placements") - .Permission(Permissions.ManagePlacements) - .LocalNav() - ) - ); + builder + .Add(S["Design"], design => design + .Add(S["Placements"], S["Placements"].PrefixPosition(), import => import + .Action("Index", "Admin", "OrchardCore.Placements") + .Permission(Permissions.ManagePlacements) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs index 674c73ec045..200cf87866e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Controllers/AdminController.cs @@ -21,323 +21,322 @@ using OrchardCore.Placements.ViewModels; using OrchardCore.Routing; -namespace OrchardCore.Placements.Controllers +namespace OrchardCore.Placements.Controllers; + +[Admin("Placements/{action}/{shapeType?}", "Placements.{action}")] +public class AdminController : Controller { - [Admin("Placements/{action}/{shapeType?}", "Placements.{action}")] - public class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly ILogger _logger; + private readonly IAuthorizationService _authorizationService; + private readonly PlacementsManager _placementsManager; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + private readonly PagerOptions _pagerOptions; + + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + + public AdminController( + ILogger logger, + IAuthorizationService authorizationService, + PlacementsManager placementsManager, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + INotifier notifier, + IOptions pagerOptions, + IShapeFactory shapeFactory) + { + _logger = logger; + _authorizationService = authorizationService; + _placementsManager = placementsManager; + _notifier = notifier; + _shapeFactory = shapeFactory; + _pagerOptions = pagerOptions.Value; + + H = htmlLocalizer; + S = stringLocalizer; + } + + [Admin("Placements", "Placements.Index")] + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly ILogger _logger; - private readonly IAuthorizationService _authorizationService; - private readonly PlacementsManager _placementsManager; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - private readonly PagerOptions _pagerOptions; - - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - - public AdminController( - ILogger logger, - IAuthorizationService authorizationService, - PlacementsManager placementsManager, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer, - INotifier notifier, - IOptions pagerOptions, - IShapeFactory shapeFactory) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) { - _logger = logger; - _authorizationService = authorizationService; - _placementsManager = placementsManager; - _notifier = notifier; - _shapeFactory = shapeFactory; - _pagerOptions = pagerOptions.Value; - - H = htmlLocalizer; - S = stringLocalizer; + return Forbid(); } - [Admin("Placements", "Placements.Index")] - public async Task Index(ContentOptions options, PagerParameters pagerParameters) + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + + var shapeTypes = await _placementsManager.ListShapePlacementsAsync(); + + var shapeList = shapeTypes.Select(entry => new ShapePlacementViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) - { - return Forbid(); - } + ShapeType = entry.Key + }).ToList(); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + if (!string.IsNullOrWhiteSpace(options.Search)) + { + shapeList = shapeList.Where(x => x.ShapeType.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - var shapeTypes = await _placementsManager.ListShapePlacementsAsync(); + var count = shapeList.Count; - var shapeList = shapeTypes.Select(entry => new ShapePlacementViewModel - { - ShapeType = entry.Key - }).ToList(); + shapeList = shapeList.OrderBy(x => x.ShapeType) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize).ToList(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - shapeList = shapeList.Where(x => x.ShapeType.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - var count = shapeList.Count; + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - shapeList = shapeList.OrderBy(x => x.ShapeType) - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize).ToList(); + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + var model = new ListShapePlacementsViewModel + { + ShapePlacements = shapeList, + Pager = pagerShape, + Options = options, + }; - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + return View(model); + } - var model = new ListShapePlacementsViewModel - { - ShapePlacements = shapeList, - Pager = pagerShape, - Options = options, - }; + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(ListShapePlacementsViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + public async Task Create(string suggestion, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + { + return Forbid(); + } + + var template = new PlacementNode[] { new() }; - return View(model); + var viewModel = new EditShapePlacementViewModel + { + Creating = true, + ShapeType = suggestion, + Nodes = JConvert.SerializeObject(template, JOptions.Indented) + }; + + ViewData["ReturnUrl"] = returnUrl; + return View(nameof(Edit), viewModel); + } + + public async Task Edit(string shapeType, string displayType = null, string contentType = null, string contentPart = null, string differentiator = null, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(ListShapePlacementsViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + var placementNodes = (await _placementsManager.GetShapePlacementsAsync(shapeType))?.ToList() ?? []; - public async Task Create(string suggestion, string returnUrl = null) + if (placementNodes.Count == 0 || ShouldCreateNode(placementNodes, displayType, contentType, contentPart, differentiator)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + var generatedNode = new PlacementNode { - return Forbid(); - } + DisplayType = displayType, + Differentiator = differentiator + }; - var template = new PlacementNode[] { new() }; + if (!string.IsNullOrEmpty(contentType)) + { + generatedNode.Filters.Add("contentType", new JsonArray(contentType)); + } - var viewModel = new EditShapePlacementViewModel + if (!string.IsNullOrEmpty(contentPart)) { - Creating = true, - ShapeType = suggestion, - Nodes = JConvert.SerializeObject(template, JOptions.Indented) - }; + generatedNode.Filters.Add("contentPart", new JsonArray(contentPart)); + } - ViewData["ReturnUrl"] = returnUrl; - return View(nameof(Edit), viewModel); + placementNodes.Add(generatedNode); } - public async Task Edit(string shapeType, string displayType = null, string contentType = null, string contentPart = null, string differentiator = null, string returnUrl = null) + var viewModel = new EditShapePlacementViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) - { - return Forbid(); - } + ShapeType = shapeType, + Nodes = JConvert.SerializeObject(placementNodes, JOptions.Indented) + }; - var placementNodes = (await _placementsManager.GetShapePlacementsAsync(shapeType))?.ToList() ?? []; + ViewData["ReturnUrl"] = returnUrl; + return View(viewModel); + } - if (placementNodes.Count == 0 || ShouldCreateNode(placementNodes, displayType, contentType, contentPart, differentiator)) - { - var generatedNode = new PlacementNode - { - DisplayType = displayType, - Differentiator = differentiator - }; - - if (!string.IsNullOrEmpty(contentType)) - { - generatedNode.Filters.Add("contentType", new JsonArray(contentType)); - } - - if (!string.IsNullOrEmpty(contentPart)) - { - generatedNode.Filters.Add("contentPart", new JsonArray(contentPart)); - } - - placementNodes.Add(generatedNode); - } + [HttpPost, ActionName(nameof(Edit))] + public async Task Edit(EditShapePlacementViewModel viewModel, string submit, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + { + return Forbid(); + } - var viewModel = new EditShapePlacementViewModel - { - ShapeType = shapeType, - Nodes = JConvert.SerializeObject(placementNodes, JOptions.Indented) - }; + ViewData["ReturnUrl"] = returnUrl; - ViewData["ReturnUrl"] = returnUrl; + if (viewModel.Creating && await _placementsManager.GetShapePlacementsAsync(viewModel.ShapeType) != null) + { + // Prevent overriding existing rules on creation. + await _notifier.WarningAsync(H["Placement rules for \"{0}\" already exists. Please edit existing rule.", viewModel.ShapeType]); return View(viewModel); } - [HttpPost, ActionName(nameof(Edit))] - public async Task Edit(EditShapePlacementViewModel viewModel, string submit, string returnUrl = null) + try { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) - { - return Forbid(); - } + var placementNodes = JConvert.DeserializeObject(viewModel.Nodes) + ?? Enumerable.Empty(); - ViewData["ReturnUrl"] = returnUrl; + // Remove empty nodes. + placementNodes = placementNodes.Where(node => !IsEmpty(node)); - if (viewModel.Creating && await _placementsManager.GetShapePlacementsAsync(viewModel.ShapeType) != null) + if (placementNodes.Any()) { - // Prevent overriding existing rules on creation. - await _notifier.WarningAsync(H["Placement rules for \"{0}\" already exists. Please edit existing rule.", viewModel.ShapeType]); - return View(viewModel); - } + // Save. + await _placementsManager.UpdateShapePlacementsAsync(viewModel.ShapeType, placementNodes); + viewModel.Creating = false; - try - { - var placementNodes = JConvert.DeserializeObject(viewModel.Nodes) - ?? Enumerable.Empty(); - - // Remove empty nodes. - placementNodes = placementNodes.Where(node => !IsEmpty(node)); - - if (placementNodes.Any()) - { - // Save. - await _placementsManager.UpdateShapePlacementsAsync(viewModel.ShapeType, placementNodes); - viewModel.Creating = false; - - await _notifier.SuccessAsync(H["The \"{0}\" placement have been saved.", viewModel.ShapeType]); - } - else if (viewModel.Creating) - { - await _notifier.WarningAsync(H["The \"{0}\" placement is empty.", viewModel.ShapeType]); - return View(viewModel); - } - else - { - // Remove if empty. - await _placementsManager.RemoveShapePlacementsAsync(viewModel.ShapeType); - await _notifier.SuccessAsync(H["The \"{0}\" placement has been deleted.", viewModel.ShapeType]); - } + await _notifier.SuccessAsync(H["The \"{0}\" placement have been saved.", viewModel.ShapeType]); } - catch (JsonException jsonException) + else if (viewModel.Creating) { - await _notifier.ErrorAsync(H["An error occurred while parsing the placement
{0}", jsonException.Message]); + await _notifier.WarningAsync(H["The \"{0}\" placement is empty.", viewModel.ShapeType]); return View(viewModel); } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while saving the placement."]); - _logger.LogError(e, "An error occurred while saving the placement."); - return View(viewModel); - } - - if (submit != "SaveAndContinue") + else { - return RedirectToReturnUrlOrIndex(returnUrl); + // Remove if empty. + await _placementsManager.RemoveShapePlacementsAsync(viewModel.ShapeType); + await _notifier.SuccessAsync(H["The \"{0}\" placement has been deleted.", viewModel.ShapeType]); } - + } + catch (JsonException jsonException) + { + await _notifier.ErrorAsync(H["An error occurred while parsing the placement
{0}", jsonException.Message]); return View(viewModel); } - - [HttpPost, ActionName(nameof(Delete))] - public async Task Delete(string shapeType, string returnUrl = null) + catch (Exception e) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) - { - return Forbid(); - } - - await _placementsManager.RemoveShapePlacementsAsync(shapeType); - await _notifier.SuccessAsync(H["The \"{0}\" placement has been deleted.", shapeType]); + await _notifier.ErrorAsync(H["An error occurred while saving the placement."]); + _logger.LogError(e, "An error occurred while saving the placement."); + return View(viewModel); + } + if (submit != "SaveAndContinue") + { return RedirectToReturnUrlOrIndex(returnUrl); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + return View(viewModel); + } + + [HttpPost, ActionName(nameof(Delete))] + public async Task Delete(string shapeType, string returnUrl = null) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) - { - return Forbid(); - } + return Forbid(); + } - if (itemIds?.Count() > 0) - { - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in itemIds) - { - await _placementsManager.RemoveShapePlacementsAsync(item); - } - await _notifier.SuccessAsync(H["Placements successfully removed."]); - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } - } + await _placementsManager.RemoveShapePlacementsAsync(shapeType); + await _notifier.SuccessAsync(H["The \"{0}\" placement has been deleted.", shapeType]); - return RedirectToAction(nameof(Index)); + return RedirectToReturnUrlOrIndex(returnUrl); + } + + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManagePlacements)) + { + return Forbid(); } - private IActionResult RedirectToReturnUrlOrIndex(string returnUrl) + if (itemIds?.Count() > 0) { - if ((string.IsNullOrEmpty(returnUrl) == false) && (Url.IsLocalUrl(returnUrl))) - { - return this.Redirect(returnUrl, true); - } - else + switch (options.BulkAction) { - return RedirectToAction(nameof(Index)); + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in itemIds) + { + await _placementsManager.RemoveShapePlacementsAsync(item); + } + await _notifier.SuccessAsync(H["Placements successfully removed."]); + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } } - private static bool ShouldCreateNode(IEnumerable nodes, string displayType, string contentType, string contentPart, string differentiator) + return RedirectToAction(nameof(Index)); + } + + private IActionResult RedirectToReturnUrlOrIndex(string returnUrl) + { + if ((string.IsNullOrEmpty(returnUrl) == false) && (Url.IsLocalUrl(returnUrl))) { - if (string.IsNullOrEmpty(displayType) && string.IsNullOrEmpty(differentiator)) - { - return false; - } - else - { - return !nodes.Any(node => - (string.IsNullOrEmpty(displayType) || node.DisplayType == displayType) && - (string.IsNullOrEmpty(contentType) || (node.Filters.ContainsKey("contentType") && FilterEquals(node.Filters["contentType"], contentType))) && - (string.IsNullOrEmpty(contentPart) || (node.Filters.ContainsKey("contentPart") && FilterEquals(node.Filters["contentPart"], contentPart))) && - (string.IsNullOrEmpty(differentiator) || node.Differentiator == differentiator)); - } + return this.Redirect(returnUrl, true); } + else + { + return RedirectToAction(nameof(Index)); + } + } - private static bool IsEmpty(PlacementNode node) + private static bool ShouldCreateNode(IEnumerable nodes, string displayType, string contentType, string contentPart, string differentiator) + { + if (string.IsNullOrEmpty(displayType) && string.IsNullOrEmpty(differentiator)) { - return string.IsNullOrEmpty(node.Location) - && string.IsNullOrEmpty(node.ShapeType) - && (node.Alternates == null || node.Alternates.Length == 0) - && (node.Wrappers == null || node.Wrappers.Length == 0); + return false; } + else + { + return !nodes.Any(node => + (string.IsNullOrEmpty(displayType) || node.DisplayType == displayType) && + (string.IsNullOrEmpty(contentType) || (node.Filters.ContainsKey("contentType") && FilterEquals(node.Filters["contentType"], contentType))) && + (string.IsNullOrEmpty(contentPart) || (node.Filters.ContainsKey("contentPart") && FilterEquals(node.Filters["contentPart"], contentPart))) && + (string.IsNullOrEmpty(differentiator) || node.Differentiator == differentiator)); + } + } + + private static bool IsEmpty(PlacementNode node) + { + return string.IsNullOrEmpty(node.Location) + && string.IsNullOrEmpty(node.ShapeType) + && (node.Alternates == null || node.Alternates.Length == 0) + && (node.Wrappers == null || node.Wrappers.Length == 0); + } - private static bool FilterEquals(object node, string value) + private static bool FilterEquals(object node, string value) + { + var jsonNode = JNode.FromObject(node); + if (jsonNode is JsonArray jsonArray) { - var jsonNode = JNode.FromObject(node); - if (jsonNode is JsonArray jsonArray) - { - var tokenValues = jsonArray.Values(); - return tokenValues.Count() == 1 && tokenValues.First() == value; - } - else - { - return jsonNode.Value() == value; - } + var tokenValues = jsonArray.Values(); + return tokenValues.Count() == 1 && tokenValues.First() == value; + } + else + { + return jsonNode.Value() == value; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentSource.cs index be7bbb50065..54b181a23b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentSource.cs @@ -6,41 +6,40 @@ using OrchardCore.Json; using OrchardCore.Placements.Services; -namespace OrchardCore.Placements.Deployment +namespace OrchardCore.Placements.Deployment; + +public class PlacementsDeploymentSource : IDeploymentSource { - public class PlacementsDeploymentSource : IDeploymentSource + private readonly PlacementsManager _placementsManager; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public PlacementsDeploymentSource( + PlacementsManager placementsManager, + IOptions jsonSerializerOptions) { - private readonly PlacementsManager _placementsManager; - private readonly JsonSerializerOptions _jsonSerializerOptions; + _placementsManager = placementsManager; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + } - public PlacementsDeploymentSource( - PlacementsManager placementsManager, - IOptions jsonSerializerOptions) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not PlacementsDeploymentStep) { - _placementsManager = placementsManager; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not PlacementsDeploymentStep) - { - return; - } - - var placementObjects = new JsonObject(); - var placements = await _placementsManager.ListShapePlacementsAsync(); - - foreach (var placement in placements) - { - placementObjects[placement.Key] = JArray.FromObject(placement.Value, _jsonSerializerOptions); - } + var placementObjects = new JsonObject(); + var placements = await _placementsManager.ListShapePlacementsAsync(); - result.Steps.Add(new JsonObject - { - ["name"] = "Placements", - ["Placements"] = placementObjects, - }); + foreach (var placement in placements) + { + placementObjects[placement.Key] = JArray.FromObject(placement.Value, _jsonSerializerOptions); } + + result.Steps.Add(new JsonObject + { + ["name"] = "Placements", + ["Placements"] = placementObjects, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStep.cs index f612747c1ba..ef8cc96a5b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Placements.Deployment +namespace OrchardCore.Placements.Deployment; + +/// +/// Adds placements to a . +/// +public class PlacementsDeploymentStep : DeploymentStep { - /// - /// Adds placements to a . - /// - public class PlacementsDeploymentStep : DeploymentStep + public PlacementsDeploymentStep() { - public PlacementsDeploymentStep() - { - Name = "Placements"; - } + Name = "Placements"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStepDriver.cs index de344a187f4..5c656ff6ec4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Deployment/PlacementsDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Placements.Deployment +namespace OrchardCore.Placements.Deployment; + +public sealed class PlacementsDeploymentStepDriver : DisplayDriver { - public sealed class PlacementsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(PlacementsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(PlacementsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("PlacementsDeploymentStep_Summary", step).Location("Summary", "Content"), - View("PlacementsDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("PlacementsDeploymentStep_Summary", step).Location("Summary", "Content"), + View("PlacementsDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(PlacementsDeploymentStep step, BuildEditorContext context) - { - return View("PlacementsDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(PlacementsDeploymentStep step, BuildEditorContext context) + { + return View("PlacementsDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Models/PlacementsDocument.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Models/PlacementsDocument.cs index 98412581b48..d840cfd76f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Models/PlacementsDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Models/PlacementsDocument.cs @@ -3,10 +3,9 @@ using OrchardCore.Data.Documents; using OrchardCore.DisplayManagement.Descriptors.ShapePlacementStrategy; -namespace OrchardCore.Placements.Models +namespace OrchardCore.Placements.Models; + +public class PlacementsDocument : Document { - public class PlacementsDocument : Document - { - public Dictionary Placements { get; init; } = new(StringComparer.OrdinalIgnoreCase); - } + public Dictionary Placements { get; init; } = new(StringComparer.OrdinalIgnoreCase); } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Recipes/PlacementStep.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Recipes/PlacementStep.cs index dd340b6af72..83abe1c6819 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Recipes/PlacementStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Recipes/PlacementStep.cs @@ -6,36 +6,35 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Placements.Recipes +namespace OrchardCore.Placements.Recipes; + +/// +/// This recipe step creates a set of placements. +/// +public sealed class PlacementStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of placements. - /// - public sealed class PlacementStep : IRecipeStepHandler + private readonly PlacementsManager _placementsManager; + + public PlacementStep(PlacementsManager placementsManager) { - private readonly PlacementsManager _placementsManager; + _placementsManager = placementsManager; + } - public PlacementStep(PlacementsManager placementsManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Placements", StringComparison.OrdinalIgnoreCase)) { - _placementsManager = placementsManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + if (context.Step.TryGetPropertyValue("Placements", out var jsonNode) && jsonNode is JsonObject templates) { - if (!string.Equals(context.Name, "Placements", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (context.Step.TryGetPropertyValue("Placements", out var jsonNode) && jsonNode is JsonObject templates) + foreach (var property in templates) { - foreach (var property in templates) - { - var name = property.Key; - var value = property.Value.ToObject(); + var name = property.Key; + var value = property.Value.ToObject(); - await _placementsManager.UpdateShapePlacementsAsync(name, value); - } + await _placementsManager.UpdateShapePlacementsAsync(name, value); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Services/DatabasePlacementsStore.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Services/DatabasePlacementsStore.cs index a101c3b91d4..37dffeee647 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Services/DatabasePlacementsStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Services/DatabasePlacementsStore.cs @@ -2,24 +2,23 @@ using OrchardCore.Documents; using OrchardCore.Placements.Models; -namespace OrchardCore.Placements.Services +namespace OrchardCore.Placements.Services; + +public class DatabasePlacementsStore : IPlacementStore { - public class DatabasePlacementsStore : IPlacementStore - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public DatabasePlacementsStore(IDocumentManager documentManager) - { - _documentManager = documentManager; - } + public DatabasePlacementsStore(IDocumentManager documentManager) + { + _documentManager = documentManager; + } - /// - public Task LoadPlacementsAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + public Task LoadPlacementsAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - public Task GetPlacementsAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + public Task GetPlacementsAsync() => _documentManager.GetOrCreateImmutableAsync(); - /// - public Task SavePlacementsAsync(PlacementsDocument placementsDocument) => _documentManager.UpdateAsync(placementsDocument); - } + /// + public Task SavePlacementsAsync(PlacementsDocument placementsDocument) => _documentManager.UpdateAsync(placementsDocument); } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Services/FilePlacementsStore.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Services/FilePlacementsStore.cs index 28401a571fc..eeb1bc84826 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Services/FilePlacementsStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Services/FilePlacementsStore.cs @@ -3,24 +3,23 @@ using OrchardCore.Documents; using OrchardCore.Placements.Models; -namespace OrchardCore.Placements.Services +namespace OrchardCore.Placements.Services; + +public class FilePlacementsStore : IPlacementStore { - public class FilePlacementsStore : IPlacementStore - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public FilePlacementsStore(IDocumentManager documentManager) - { - _documentManager = documentManager; - } + public FilePlacementsStore(IDocumentManager documentManager) + { + _documentManager = documentManager; + } - /// - public Task LoadPlacementsAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + public Task LoadPlacementsAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - public Task GetPlacementsAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + public Task GetPlacementsAsync() => _documentManager.GetOrCreateImmutableAsync(); - /// - public Task SavePlacementsAsync(PlacementsDocument placementsDocument) => _documentManager.UpdateAsync(placementsDocument); - } + /// + public Task SavePlacementsAsync(PlacementsDocument placementsDocument) => _documentManager.UpdateAsync(placementsDocument); } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Services/IPlacementStore.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Services/IPlacementStore.cs index eed22816293..59a4461bdff 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Services/IPlacementStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Services/IPlacementStore.cs @@ -1,23 +1,22 @@ using System.Threading.Tasks; using OrchardCore.Placements.Models; -namespace OrchardCore.Placements.Services +namespace OrchardCore.Placements.Services; + +public interface IPlacementStore { - public interface IPlacementStore - { - /// - /// Loads the placements document from the store for updating and that should not be cached. - /// - Task LoadPlacementsAsync(); + /// + /// Loads the placements document from the store for updating and that should not be cached. + /// + Task LoadPlacementsAsync(); - /// - /// Gets the placements document from the cache for sharing and that should not be updated. - /// - Task GetPlacementsAsync(); + /// + /// Gets the placements document from the cache for sharing and that should not be updated. + /// + Task GetPlacementsAsync(); - /// - /// Updates the store with the provided placements document and then updates the cache. - /// - Task SavePlacementsAsync(PlacementsDocument placementsDocument); - } + /// + /// Updates the store with the provided placements document and then updates the cache. + /// + Task SavePlacementsAsync(PlacementsDocument placementsDocument); } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementProvider.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementProvider.cs index b39dd7ed118..06901e3297f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementProvider.cs @@ -7,102 +7,101 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.Placements.Services +namespace OrchardCore.Placements.Services; + +public class PlacementProvider : IShapePlacementProvider { - public class PlacementProvider : IShapePlacementProvider + private readonly PlacementsManager _placementsManager; + private readonly IEnumerable _placementNodeFilterProviders; + + public PlacementProvider( + PlacementsManager placementsManager, + IEnumerable placementNodeFilterProviders) + { + _placementsManager = placementsManager; + _placementNodeFilterProviders = placementNodeFilterProviders; + } + + public async Task BuildPlacementInfoResolverAsync(IBuildShapeContext context) { - private readonly PlacementsManager _placementsManager; + var placements = await _placementsManager.ListShapePlacementsAsync(); + return new PlacementInfoResolver(placements, _placementNodeFilterProviders); + } + + public class PlacementInfoResolver : IPlacementInfoResolver + { + private readonly IReadOnlyDictionary _placements; private readonly IEnumerable _placementNodeFilterProviders; - public PlacementProvider( - PlacementsManager placementsManager, + public PlacementInfoResolver( + IReadOnlyDictionary placements, IEnumerable placementNodeFilterProviders) { - _placementsManager = placementsManager; + _placements = placements; _placementNodeFilterProviders = placementNodeFilterProviders; } - public async Task BuildPlacementInfoResolverAsync(IBuildShapeContext context) + public PlacementInfo ResolvePlacement(ShapePlacementContext placementContext) { - var placements = await _placementsManager.ListShapePlacementsAsync(); - return new PlacementInfoResolver(placements, _placementNodeFilterProviders); - } + PlacementInfo placement = null; - public class PlacementInfoResolver : IPlacementInfoResolver - { - private readonly IReadOnlyDictionary _placements; - private readonly IEnumerable _placementNodeFilterProviders; - - public PlacementInfoResolver( - IReadOnlyDictionary placements, - IEnumerable placementNodeFilterProviders) + if (_placements.ContainsKey(placementContext.ShapeType)) { - _placements = placements; - _placementNodeFilterProviders = placementNodeFilterProviders; - } + var shapePlacements = _placements[placementContext.ShapeType]; - public PlacementInfo ResolvePlacement(ShapePlacementContext placementContext) - { - PlacementInfo placement = null; - - if (_placements.ContainsKey(placementContext.ShapeType)) + foreach (var placementRule in shapePlacements) { - var shapePlacements = _placements[placementContext.ShapeType]; + var filters = placementRule.Filters.ToList(); + + Func predicate = ctx => CheckFilter(ctx, placementRule); - foreach (var placementRule in shapePlacements) + if (filters.Count > 0) { - var filters = placementRule.Filters.ToList(); - - Func predicate = ctx => CheckFilter(ctx, placementRule); - - if (filters.Count > 0) - { - predicate = filters.Aggregate(predicate, BuildPredicate); - } - - if (!predicate(placementContext)) - { - // Ignore rule - continue; - } - - placement ??= new PlacementInfo - { - Source = "OrchardCore.Placements", - }; - - if (!string.IsNullOrEmpty(placementRule.Location)) - { - placement.Location = placementRule.Location; - } - - if (!string.IsNullOrEmpty(placementRule.ShapeType)) - { - placement.ShapeType = placementRule.ShapeType; - } - - if (placementRule.Alternates?.Length > 0) - { - placement.Alternates = placement.Alternates.Combine(new AlternatesCollection(placementRule.Alternates)); - } - - if (placementRule.Wrappers?.Length > 0) - { - placement.Wrappers = placement.Wrappers.Combine(new AlternatesCollection(placementRule.Wrappers)); - } + predicate = filters.Aggregate(predicate, BuildPredicate); } - } - return placement; - } + if (!predicate(placementContext)) + { + // Ignore rule + continue; + } - private static bool CheckFilter(ShapePlacementContext ctx, PlacementNode filter) => ShapePlacementParsingStrategy.CheckFilter(ctx, filter); + placement ??= new PlacementInfo + { + Source = "OrchardCore.Placements", + }; - private Func BuildPredicate(Func predicate, - KeyValuePair term) - { - return ShapePlacementParsingStrategy.BuildPredicate(predicate, term, _placementNodeFilterProviders); + if (!string.IsNullOrEmpty(placementRule.Location)) + { + placement.Location = placementRule.Location; + } + + if (!string.IsNullOrEmpty(placementRule.ShapeType)) + { + placement.ShapeType = placementRule.ShapeType; + } + + if (placementRule.Alternates?.Length > 0) + { + placement.Alternates = placement.Alternates.Combine(new AlternatesCollection(placementRule.Alternates)); + } + + if (placementRule.Wrappers?.Length > 0) + { + placement.Wrappers = placement.Wrappers.Combine(new AlternatesCollection(placementRule.Wrappers)); + } + } } + + return placement; + } + + private static bool CheckFilter(ShapePlacementContext ctx, PlacementNode filter) => ShapePlacementParsingStrategy.CheckFilter(ctx, filter); + + private Func BuildPredicate(Func predicate, + KeyValuePair term) + { + return ShapePlacementParsingStrategy.BuildPredicate(predicate, term, _placementNodeFilterProviders); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementsManager.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementsManager.cs index ce9d47ff7d2..55d6c88177b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementsManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Services/PlacementsManager.cs @@ -3,55 +3,54 @@ using System.Threading.Tasks; using OrchardCore.DisplayManagement.Descriptors.ShapePlacementStrategy; -namespace OrchardCore.Placements.Services +namespace OrchardCore.Placements.Services; + +public sealed class PlacementsManager { - public sealed class PlacementsManager + private readonly IPlacementStore _placementStore; + + public PlacementsManager(IPlacementStore placementStore) { - private readonly IPlacementStore _placementStore; + _placementStore = placementStore; + } - public PlacementsManager(IPlacementStore placementStore) - { - _placementStore = placementStore; - } + public async Task> ListShapePlacementsAsync() + { + var document = await _placementStore.GetPlacementsAsync(); - public async Task> ListShapePlacementsAsync() - { - var document = await _placementStore.GetPlacementsAsync(); + return document.Placements; + } - return document.Placements; - } + public async Task GetShapePlacementsAsync(string shapeType) + { + var document = await _placementStore.GetPlacementsAsync(); - public async Task GetShapePlacementsAsync(string shapeType) + if (document.Placements.TryGetValue(shapeType, out var nodes)) { - var document = await _placementStore.GetPlacementsAsync(); - - if (document.Placements.TryGetValue(shapeType, out var nodes)) - { - return nodes; - } - else - { - return null; - } + return nodes; } - - public async Task UpdateShapePlacementsAsync(string shapeType, IEnumerable placementNodes) + else { - var document = await _placementStore.LoadPlacementsAsync(); + return null; + } + } - document.Placements[shapeType] = placementNodes.ToArray(); + public async Task UpdateShapePlacementsAsync(string shapeType, IEnumerable placementNodes) + { + var document = await _placementStore.LoadPlacementsAsync(); - await _placementStore.SavePlacementsAsync(document); - } + document.Placements[shapeType] = placementNodes.ToArray(); - public async Task RemoveShapePlacementsAsync(string shapeType) - { - var document = await _placementStore.LoadPlacementsAsync(); + await _placementStore.SavePlacementsAsync(document); + } + + public async Task RemoveShapePlacementsAsync(string shapeType) + { + var document = await _placementStore.LoadPlacementsAsync(); - if (document.Placements.Remove(shapeType)) - { - await _placementStore.SavePlacementsAsync(document); - } + if (document.Placements.Remove(shapeType)) + { + await _placementStore.SavePlacementsAsync(document); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs index 25af5c81c26..e007ac88e50 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartDefinitionDriver.cs @@ -6,54 +6,53 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Placements.ViewModels; -namespace OrchardCore.Placements.Settings +namespace OrchardCore.Placements.Settings; + +public sealed class PlacementContentPartDefinitionDriver : ContentPartDefinitionDisplayDriver { - public sealed class PlacementContentPartDefinitionDriver : ContentPartDefinitionDisplayDriver + internal readonly IStringLocalizer S; + + public PlacementContentPartDefinitionDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public PlacementContentPartDefinitionDriver(IStringLocalizer localizer) - { - S = localizer; - } + public override IDisplayResult Edit(ContentPartDefinition contentPartDefinition, BuildEditorContext context) + { + var displayName = contentPartDefinition.DisplayName(); - public override IDisplayResult Edit(ContentPartDefinition contentPartDefinition, BuildEditorContext context) + return Initialize("PlacementSettings", model => { - var displayName = contentPartDefinition.DisplayName(); - - return Initialize("PlacementSettings", model => - { - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = contentPartDefinition.Name, - Description = S["Placement for a {0} part", displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = contentPartDefinition.Name, - DisplayType = "Detail", - Description = S["Placement for a {0} part in detail views", displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = contentPartDefinition.Name, - DisplayType = "Summary", - Description = S["Placement for a {0} part in summary views", displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = $"{contentPartDefinition.Name}_Edit", - Description = S["Placement in admin editor for a {0} part", displayName] - }); - - }).Location("Shortcuts"); - } + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = contentPartDefinition.Name, + Description = S["Placement for a {0} part", displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = contentPartDefinition.Name, + DisplayType = "Detail", + Description = S["Placement for a {0} part in detail views", displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = contentPartDefinition.Name, + DisplayType = "Summary", + Description = S["Placement for a {0} part in summary views", displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{contentPartDefinition.Name}_Edit", + Description = S["Placement in admin editor for a {0} part", displayName] + }); + + }).Location("Shortcuts"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs index e73a44a5c67..3386b946e3b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentPartFieldDefinitionDisplayDriver.cs @@ -5,61 +5,60 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Placements.ViewModels; -namespace OrchardCore.Placements.Settings +namespace OrchardCore.Placements.Settings; + +public sealed class PlacementContentPartFieldDefinitionDisplayDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class PlacementContentPartFieldDefinitionDisplayDriver : ContentPartFieldDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public PlacementContentPartFieldDefinitionDisplayDriver(IStringLocalizer localizer) - { - S = localizer; - } + public PlacementContentPartFieldDefinitionDisplayDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Edit(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext context) + { + return Initialize("PlacementSettings", model => { - return Initialize("PlacementSettings", model => - { - var shapeType = contentPartFieldDefinition.FieldDefinition.Name; - var partName = contentPartFieldDefinition.PartDefinition.Name; - var differentiator = $"{contentPartFieldDefinition.PartDefinition.Name}-{contentPartFieldDefinition.Name}"; - var displayName = contentPartFieldDefinition.DisplayName(); + var shapeType = contentPartFieldDefinition.FieldDefinition.Name; + var partName = contentPartFieldDefinition.PartDefinition.Name; + var differentiator = $"{contentPartFieldDefinition.PartDefinition.Name}-{contentPartFieldDefinition.Name}"; + var displayName = contentPartFieldDefinition.DisplayName(); - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = shapeType, - Differentiator = differentiator, - Description = S["Placement for the {0} field in a {1}", displayName, partName] - }); + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = shapeType, + Differentiator = differentiator, + Description = S["Placement for the {0} field in a {1}", displayName, partName] + }); - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = shapeType, - Differentiator = differentiator, - DisplayType = "Detail", - Description = S["Placement for the {0} field in a {1} in detail views", displayName, partName] - }); + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = shapeType, + Differentiator = differentiator, + DisplayType = "Detail", + Description = S["Placement for the {0} field in a {1} in detail views", displayName, partName] + }); - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = shapeType, - Differentiator = differentiator, - DisplayType = "Summary", - Description = S["Placement for the {0} field in a {1} in summary views", displayName, partName] - }); + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = shapeType, + Differentiator = differentiator, + DisplayType = "Summary", + Description = S["Placement for the {0} field in a {1} in summary views", displayName, partName] + }); - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = $"{shapeType}_Edit", - Differentiator = differentiator, - Description = S["Placement in admin editor for the {0} field in a {1}", displayName, partName] - }); + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{shapeType}_Edit", + Differentiator = differentiator, + Description = S["Placement in admin editor for the {0} field in a {1}", displayName, partName] + }); - }).Location("Shortcuts"); - } + }).Location("Shortcuts"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs index d197cac9fb3..8f53f8567a9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Settings/PlacementContentTypePartDefinitionDriver.cs @@ -5,60 +5,59 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Placements.ViewModels; -namespace OrchardCore.Placements.Settings +namespace OrchardCore.Placements.Settings; + +public sealed class PlacementContentTypePartDefinitionDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class PlacementContentTypePartDefinitionDriver : ContentTypePartDefinitionDisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public PlacementContentTypePartDefinitionDriver(IStringLocalizer localizer) - { - S = localizer; - } + public PlacementContentTypePartDefinitionDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + return Initialize("PlacementSettings", model => { - return Initialize("PlacementSettings", model => - { - var contentType = contentTypePartDefinition.ContentTypeDefinition.Name; - var partName = contentTypePartDefinition.Name; - var displayName = contentTypePartDefinition.ContentTypeDefinition.DisplayName; - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = partName, - ContentType = contentType, - Description = S["Placement for the {0} part in a {1} type", partName, displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = partName, - ContentType = contentType, - DisplayType = "Detail", - Description = S["Placement for the {0} part in a {1} type in detail views", partName, displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = partName, - ContentType = contentType, - DisplayType = "Summary", - Description = S["Placement for the {0} part in a {1} type in summary views", partName, displayName] - }); - - model.ContentSettingsEntries.Add( - new ContentSettingsEntry - { - ShapeType = $"{partName}_Edit", - ContentType = contentType, - Description = S["Placement in admin editor for the {0} part in a {1} type", partName, displayName] - }); - - }).Location("Shortcuts"); - } + var contentType = contentTypePartDefinition.ContentTypeDefinition.Name; + var partName = contentTypePartDefinition.Name; + var displayName = contentTypePartDefinition.ContentTypeDefinition.DisplayName; + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = partName, + ContentType = contentType, + Description = S["Placement for the {0} part in a {1} type", partName, displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = partName, + ContentType = contentType, + DisplayType = "Detail", + Description = S["Placement for the {0} part in a {1} type in detail views", partName, displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = partName, + ContentType = contentType, + DisplayType = "Summary", + Description = S["Placement for the {0} part in a {1} type in summary views", partName, displayName] + }); + + model.ContentSettingsEntries.Add( + new ContentSettingsEntry + { + ShapeType = $"{partName}_Edit", + ContentType = contentType, + Description = S["Placement in admin editor for the {0} part in a {1} type", partName, displayName] + }); + + }).Location("Shortcuts"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Placements/Startup.cs index b2a041e08e6..450a8552e11 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/Startup.cs @@ -12,45 +12,44 @@ using OrchardCore.Recipes; using OrchardCore.Security.Permissions; -namespace OrchardCore.Placements +namespace OrchardCore.Placements; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.TryAddScoped(); - services.AddScoped(); - services.AddScoped(); + services.TryAddScoped(); + services.AddScoped(); + services.AddScoped(); - // Shortcuts in settings - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + // Shortcuts in settings + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // Recipes - services.AddRecipeExecutionStep(); - } + // Recipes + services.AddRecipeExecutionStep(); } +} - [Feature("OrchardCore.Placements.FileStorage")] - public class FileContentDefinitionStartup : StartupBase +[Feature("OrchardCore.Placements.FileStorage")] +public class FileContentDefinitionStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.RemoveAll(); - services.AddScoped(); - } + services.RemoveAll(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ContentSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ContentSettingsViewModel.cs index 60c4afdd19d..782232f0bf5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ContentSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ContentSettingsViewModel.cs @@ -1,20 +1,19 @@ using System.Collections.Generic; using Microsoft.Extensions.Localization; -namespace OrchardCore.Placements.ViewModels +namespace OrchardCore.Placements.ViewModels; + +public class ContentSettingsViewModel { - public class ContentSettingsViewModel - { - public List ContentSettingsEntries { get; set; } = []; - } + public List ContentSettingsEntries { get; set; } = []; +} - public class ContentSettingsEntry - { - public string ShapeType { get; set; } - public string DisplayType { get; set; } - public string ContentType { get; set; } - public string ContentPart { get; set; } - public string Differentiator { get; set; } - public LocalizedString Description { get; set; } - } +public class ContentSettingsEntry +{ + public string ShapeType { get; set; } + public string DisplayType { get; set; } + public string ContentType { get; set; } + public string ContentPart { get; set; } + public string Differentiator { get; set; } + public LocalizedString Description { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/EditShapePlacementViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/EditShapePlacementViewModel.cs index 62b93ebedd9..d850dbf0138 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/EditShapePlacementViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/EditShapePlacementViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Placements.ViewModels +namespace OrchardCore.Placements.ViewModels; + +public class EditShapePlacementViewModel { - public class EditShapePlacementViewModel - { - public bool Creating { get; set; } + public bool Creating { get; set; } - public string ShapeType { get; set; } + public string ShapeType { get; set; } - public string Nodes { get; set; } - } + public string Nodes { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs index aa3709a78a3..cc83226580b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ListShapePlacementsViewModel.cs @@ -2,31 +2,30 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Placements.ViewModels +namespace OrchardCore.Placements.ViewModels; + +public class ListShapePlacementsViewModel { - public class ListShapePlacementsViewModel - { - public IList ShapePlacements { get; set; } - public dynamic Pager { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - } + public IList ShapePlacements { get; set; } + public dynamic Pager { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); +} - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } - #region Lists to populate + #region Lists to populate - [BindNever] - public List ContentsBulkAction { get; set; } + [BindNever] + public List ContentsBulkAction { get; set; } - #endregion Lists to populate - } + #endregion Lists to populate +} - public enum ContentsBulkAction - { - None, - Remove - } +public enum ContentsBulkAction +{ + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ShapePlacementViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ShapePlacementViewModel.cs index 2c8e0625ae7..c262fc3ebd4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ShapePlacementViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Placements/ViewModels/ShapePlacementViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Placements.ViewModels +namespace OrchardCore.Placements.ViewModels; + +public class ShapePlacementViewModel { - public class ShapePlacementViewModel - { - public string ShapeType { get; set; } - } + public string ShapeType { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.PublishLater/Drivers/PublishLaterPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.PublishLater/Drivers/PublishLaterPartDisplayDriver.cs index f0293c728e3..220ca1b07b9 100644 --- a/src/OrchardCore.Modules/OrchardCore.PublishLater/Drivers/PublishLaterPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.PublishLater/Drivers/PublishLaterPartDisplayDriver.cs @@ -10,68 +10,67 @@ using OrchardCore.PublishLater.Models; using OrchardCore.PublishLater.ViewModels; -namespace OrchardCore.PublishLater.Drivers +namespace OrchardCore.PublishLater.Drivers; + +public sealed class PublishLaterPartDisplayDriver : ContentPartDisplayDriver { - public sealed class PublishLaterPartDisplayDriver : ContentPartDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly ILocalClock _localClock; + + public PublishLaterPartDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + ILocalClock localClock) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly ILocalClock _localClock; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _localClock = localClock; + } - public PublishLaterPartDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - ILocalClock localClock) - { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _localClock = localClock; - } + public override IDisplayResult Display(PublishLaterPart part, BuildPartDisplayContext context) + { + return Initialize($"{nameof(PublishLaterPart)}_SummaryAdmin", + model => PopulateViewModel(part, model)) + .Location("SummaryAdmin", "Meta:25"); + } - public override IDisplayResult Display(PublishLaterPart part, BuildPartDisplayContext context) - { - return Initialize($"{nameof(PublishLaterPart)}_SummaryAdmin", - model => PopulateViewModel(part, model)) - .Location("SummaryAdmin", "Meta:25"); - } + public override IDisplayResult Edit(PublishLaterPart part, BuildPartEditorContext context) + { + return Initialize(GetEditorShapeType(context), + model => PopulateViewModel(part, model)) + .Location("Actions:10"); + } - public override IDisplayResult Edit(PublishLaterPart part, BuildPartEditorContext context) - { - return Initialize(GetEditorShapeType(context), - model => PopulateViewModel(part, model)) - .Location("Actions:10"); - } + public override async Task UpdateAsync(PublishLaterPart part, UpdatePartEditorContext context) + { + var httpContext = _httpContextAccessor.HttpContext; - public override async Task UpdateAsync(PublishLaterPart part, UpdatePartEditorContext context) + if (await _authorizationService.AuthorizeAsync(httpContext?.User, CommonPermissions.PublishContent, part.ContentItem)) { - var httpContext = _httpContextAccessor.HttpContext; - - if (await _authorizationService.AuthorizeAsync(httpContext?.User, CommonPermissions.PublishContent, part.ContentItem)) - { - var viewModel = new PublishLaterPartViewModel(); + var viewModel = new PublishLaterPartViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix); - if (viewModel.ScheduledPublishLocalDateTime == null || httpContext.Request.Form["submit.Save"] == "submit.CancelPublishLater") - { - part.ScheduledPublishUtc = null; - } - else - { - part.ScheduledPublishUtc = await _localClock.ConvertToUtcAsync(viewModel.ScheduledPublishLocalDateTime.Value); - } + if (viewModel.ScheduledPublishLocalDateTime == null || httpContext.Request.Form["submit.Save"] == "submit.CancelPublishLater") + { + part.ScheduledPublishUtc = null; + } + else + { + part.ScheduledPublishUtc = await _localClock.ConvertToUtcAsync(viewModel.ScheduledPublishLocalDateTime.Value); } - - return Edit(part, context); } - private async ValueTask PopulateViewModel(PublishLaterPart part, PublishLaterPartViewModel viewModel) - { - viewModel.ContentItem = part.ContentItem; - viewModel.ScheduledPublishUtc = part.ScheduledPublishUtc; - viewModel.ScheduledPublishLocalDateTime = part.ScheduledPublishUtc.HasValue ? - (await _localClock.ConvertToLocalAsync(part.ScheduledPublishUtc.Value)).DateTime : - null; - } + return Edit(part, context); + } + + private async ValueTask PopulateViewModel(PublishLaterPart part, PublishLaterPartViewModel viewModel) + { + viewModel.ContentItem = part.ContentItem; + viewModel.ScheduledPublishUtc = part.ScheduledPublishUtc; + viewModel.ScheduledPublishLocalDateTime = part.ScheduledPublishUtc.HasValue ? + (await _localClock.ConvertToLocalAsync(part.ScheduledPublishUtc.Value)).DateTime : + null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.PublishLater/Startup.cs b/src/OrchardCore.Modules/OrchardCore.PublishLater/Startup.cs index cd33be08b44..434523818b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.PublishLater/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.PublishLater/Startup.cs @@ -13,28 +13,27 @@ using OrchardCore.PublishLater.Services; using OrchardCore.PublishLater.ViewModels; -namespace OrchardCore.PublishLater +namespace OrchardCore.PublishLater; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }); + o.MemberAccessStrategy.Register(); + }); - services - .AddContentPart() - .UseDisplayDriver(); + services + .AddContentPart() + .UseDisplayDriver(); - services.AddDataMigration(); + services.AddDataMigration(); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); - services.AddSingleton(); - } + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.PublishLater/ViewModels/PublishLaterPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.PublishLater/ViewModels/PublishLaterPartViewModel.cs index 5d01c036227..beb56aeec1f 100644 --- a/src/OrchardCore.Modules/OrchardCore.PublishLater/ViewModels/PublishLaterPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.PublishLater/ViewModels/PublishLaterPartViewModel.cs @@ -2,15 +2,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement; -namespace OrchardCore.PublishLater.ViewModels +namespace OrchardCore.PublishLater.ViewModels; + +public class PublishLaterPartViewModel { - public class PublishLaterPartViewModel - { - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - public DateTime? ScheduledPublishUtc { get; set; } + public DateTime? ScheduledPublishUtc { get; set; } - public DateTime? ScheduledPublishLocalDateTime { get; set; } - } + public DateTime? ScheduledPublishLocalDateTime { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Queries/AdminMenu.cs index 2bb1b22d61f..12e40d3813c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/AdminMenu.cs @@ -2,40 +2,39 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Queries +namespace OrchardCore.Queries; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Search"], NavigationConstants.AdminMenuSearchPosition, search => search - .AddClass("search") - .Id("search") - .Add(S["Queries"], S["Queries"].PrefixPosition(), queries => queries - .Add(S["All queries"], "1", allQueries => allQueries - .Action("Index", "Admin", "OrchardCore.Queries") - .AddClass("searchallqueries") - .Id("searchallqueries") - .Permission(Permissions.ManageQueries) - .LocalNav() - ) + builder + .Add(S["Search"], NavigationConstants.AdminMenuSearchPosition, search => search + .AddClass("search") + .Id("search") + .Add(S["Queries"], S["Queries"].PrefixPosition(), queries => queries + .Add(S["All queries"], "1", allQueries => allQueries + .Action("Index", "Admin", "OrchardCore.Queries") + .AddClass("searchallqueries") + .Id("searchallqueries") + .Permission(Permissions.ManageQueries) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs index c3fb7118456..f0fe7e769f8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/AdminController.cs @@ -19,267 +19,266 @@ using OrchardCore.Routing; using YesSql.Services; -namespace OrchardCore.Queries.Controllers +namespace OrchardCore.Queries.Controllers; + +[Admin("Queries/{action}/{id?}", "Queries{action}")] +public sealed class AdminController : Controller { - [Admin("Queries/{action}/{id?}", "Queries{action}")] - public sealed class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly IServiceProvider _serviceProvider; + private readonly IQueryManager _queryManager; + private readonly IDisplayManager _displayManager; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly IShapeFactory _shapeFactory; + + internal readonly IStringLocalizer S; + internal readonly IHtmlLocalizer H; + + public AdminController( + IDisplayManager displayManager, + IAuthorizationService authorizationService, + IOptions pagerOptions, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier, + IQueryManager queryManager, + IServiceProvider serviceProvider, + IUpdateModelAccessor updateModelAccessor) + { + _displayManager = displayManager; + _authorizationService = authorizationService; + _pagerOptions = pagerOptions.Value; + _queryManager = queryManager; + _serviceProvider = serviceProvider; + _updateModelAccessor = updateModelAccessor; + _shapeFactory = shapeFactory; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + } + + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly IServiceProvider _serviceProvider; - private readonly IQueryManager _queryManager; - private readonly IDisplayManager _displayManager; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly IShapeFactory _shapeFactory; - - internal readonly IStringLocalizer S; - internal readonly IHtmlLocalizer H; - - public AdminController( - IDisplayManager displayManager, - IAuthorizationService authorizationService, - IOptions pagerOptions, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier, - IQueryManager queryManager, - IServiceProvider serviceProvider, - IUpdateModelAccessor updateModelAccessor) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) { - _displayManager = displayManager; - _authorizationService = authorizationService; - _pagerOptions = pagerOptions.Value; - _queryManager = queryManager; - _serviceProvider = serviceProvider; - _updateModelAccessor = updateModelAccessor; - _shapeFactory = shapeFactory; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + var result = await _queryManager.PageQueriesAsync(pager.Page, pager.PageSize, new QueryContext() + { + Name = options.Search, + }); - var result = await _queryManager.PageQueriesAsync(pager.Page, pager.PageSize, new QueryContext() + var model = new QueriesIndexViewModel + { + Queries = [], + Options = options, + Pager = await _shapeFactory.PagerAsync(pager, result.Count, routeData), + QuerySourceNames = _serviceProvider.GetServices().Select(x => x.Name).ToArray(), + }; + + foreach (var query in result.Records) + { + model.Queries.Add(new QueryEntry { - Name = options.Search, + Query = query, + Shape = await _displayManager.BuildDisplayAsync(query, _updateModelAccessor.ModelUpdater, "SummaryAdmin") }); + } - var model = new QueriesIndexViewModel - { - Queries = [], - Options = options, - Pager = await _shapeFactory.PagerAsync(pager, result.Count, routeData), - QuerySourceNames = _serviceProvider.GetServices().Select(x => x.Name).ToArray(), - }; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - foreach (var query in result.Records) - { - model.Queries.Add(new QueryEntry - { - Query = query, - Shape = await _displayManager.BuildDisplayAsync(query, _updateModelAccessor.ModelUpdater, "SummaryAdmin") - }); - } + return View(model); + } - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + [HttpPost] + [ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(QueriesIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - return View(model); + public async Task Create(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - [HttpPost] - [ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(QueriesIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + var query = await _queryManager.NewAsync(id); - public async Task Create(string id) + if (query == null) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } - - var query = await _queryManager.NewAsync(id); + return NotFound(); + } - if (query == null) - { - return NotFound(); - } + var model = new QueriesCreateViewModel + { + Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, true), + SourceName = id + }; - var model = new QueriesCreateViewModel - { - Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, true), - SourceName = id - }; + return View(model); + } - return View(model); + [HttpPost] + [ActionName(nameof(Create))] + public async Task CreatePost(QueriesCreateViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - [HttpPost] - [ActionName(nameof(Create))] - public async Task CreatePost(QueriesCreateViewModel model) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } + var query = await _queryManager.NewAsync(model.SourceName); - var query = await _queryManager.NewAsync(model.SourceName); + if (query == null) + { + return NotFound(); + } - if (query == null) - { - return NotFound(); - } + var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, true); - var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, true); + if (ModelState.IsValid) + { + await _queryManager.SaveAsync(query); - if (ModelState.IsValid) - { - await _queryManager.SaveAsync(query); + await _notifier.SuccessAsync(H["Query created successfully."]); - await _notifier.SuccessAsync(H["Query created successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); - } + // If we got this far, something failed, redisplay form. + model.Editor = editor; - // If we got this far, something failed, redisplay form. - model.Editor = editor; + return View(model); + } - return View(model); + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - public async Task Edit(string id) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } + var query = await _queryManager.GetQueryAsync(id); - var query = await _queryManager.GetQueryAsync(id); + if (query == null) + { + return NotFound(); + } - if (query == null) - { - return NotFound(); - } + var model = new QueriesEditViewModel + { + QueryId = id, + Name = query.Name, + Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, false) + }; - var model = new QueriesEditViewModel - { - QueryId = id, - Name = query.Name, - Editor = await _displayManager.BuildEditorAsync(query, _updateModelAccessor.ModelUpdater, false) - }; + return View(model); + } - return View(model); + [HttpPost] + [ActionName(nameof(Edit))] + public async Task EditPost(QueriesEditViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - [HttpPost] - [ActionName(nameof(Edit))] - public async Task EditPost(QueriesEditViewModel model) + if (string.IsNullOrEmpty(model.QueryId)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } + return BadRequest(); + } - if (string.IsNullOrEmpty(model.QueryId)) - { - return BadRequest(); - } + var query = await _queryManager.GetQueryAsync(model.QueryId); - var query = await _queryManager.GetQueryAsync(model.QueryId); + if (query == null) + { + return NotFound(); + } - if (query == null) - { - return NotFound(); - } + var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, false); - var editor = await _displayManager.UpdateEditorAsync(query, _updateModelAccessor.ModelUpdater, false); + if (ModelState.IsValid) + { + await _queryManager.DeleteQueryAsync(model.QueryId); + await _queryManager.UpdateAsync(query); + await _notifier.SuccessAsync(H["Query updated successfully."]); - if (ModelState.IsValid) - { - await _queryManager.DeleteQueryAsync(model.QueryId); - await _queryManager.UpdateAsync(query); - await _notifier.SuccessAsync(H["Query updated successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); - } + model.Editor = editor; - model.Editor = editor; + // If we got this far, something failed, redisplay form. + return View(model); + } - // If we got this far, something failed, redisplay form. - return View(model); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string id) + if (!await _queryManager.DeleteQueryAsync(id)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) - { - return Forbid(); - } + return NotFound(); + } - if (!await _queryManager.DeleteQueryAsync(id)) - { - return NotFound(); - } + await _notifier.SuccessAsync(H["Query deleted successfully."]); - await _notifier.SuccessAsync(H["Query deleted successfully."]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + [HttpPost] + [ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + { + return Forbid(); } - [HttpPost] - [ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageQueries)) + switch (options.BulkAction) { - return Forbid(); + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + await _queryManager.DeleteQueryAsync(itemIds.ToArray()); + await _notifier.SuccessAsync(H["Queries successfully removed."]); + break; + default: + return BadRequest(); } - - if (itemIds?.Count() > 0) - { - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - await _queryManager.DeleteQueryAsync(itemIds.ToArray()); - await _notifier.SuccessAsync(H["Queries successfully removed."]); - break; - default: - return BadRequest(); - } - } - - return RedirectToAction(nameof(Index)); } + + return RedirectToAction(nameof(Index)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs index 88c7e62938f..4b938cb9d86 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Controllers/QueryApiController.cs @@ -7,60 +7,59 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace OrchardCore.Queries.Controllers +namespace OrchardCore.Queries.Controllers; + +[Route("api/queries")] +[ApiController] +[Authorize(AuthenticationSchemes = "Api")] +[IgnoreAntiforgeryToken] +[AllowAnonymous] +public sealed class QueryApiController : ControllerBase { - [Route("api/queries")] - [ApiController] - [Authorize(AuthenticationSchemes = "Api")] - [IgnoreAntiforgeryToken] - [AllowAnonymous] - public sealed class QueryApiController : ControllerBase + private readonly IAuthorizationService _authorizationService; + private readonly IQueryManager _queryManager; + + public QueryApiController( + IAuthorizationService authorizationService, + IQueryManager queryManager + ) + { + _authorizationService = authorizationService; + _queryManager = queryManager; + } + + [HttpPost, HttpGet] + [Route("{name}")] + public async Task Query( + string name, + string parameters) { - private readonly IAuthorizationService _authorizationService; - private readonly IQueryManager _queryManager; + var query = await _queryManager.GetQueryAsync(name); - public QueryApiController( - IAuthorizationService authorizationService, - IQueryManager queryManager - ) + if (query == null) { - _authorizationService = authorizationService; - _queryManager = queryManager; + return NotFound(); } - [HttpPost, HttpGet] - [Route("{name}")] - public async Task Query( - string name, - string parameters) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.CreatePermissionForQuery(query.Name))) { - var query = await _queryManager.GetQueryAsync(name); - - if (query == null) - { - return NotFound(); - } - - if (!await _authorizationService.AuthorizeAsync(User, Permissions.CreatePermissionForQuery(query.Name))) - { - // Intentionally not returning Unauthorized as it is not usable from APIs and would - // expose the existence of a named query (not a concern per se). - return NotFound(); - } + // Intentionally not returning Unauthorized as it is not usable from APIs and would + // expose the existence of a named query (not a concern per se). + return NotFound(); + } - if (Request.Method == HttpMethods.Post && string.IsNullOrEmpty(parameters)) - { - using var reader = new StreamReader(Request.Body, Encoding.UTF8); - parameters = await reader.ReadToEndAsync(); - } + if (Request.Method == HttpMethods.Post && string.IsNullOrEmpty(parameters)) + { + using var reader = new StreamReader(Request.Body, Encoding.UTF8); + parameters = await reader.ReadToEndAsync(); + } - var queryParameters = !string.IsNullOrEmpty(parameters) ? - JConvert.DeserializeObject>(parameters) - : []; + var queryParameters = !string.IsNullOrEmpty(parameters) ? + JConvert.DeserializeObject>(parameters) + : []; - var result = await _queryManager.ExecuteQueryAsync(query, queryParameters); + var result = await _queryManager.ExecuteQueryAsync(query, queryParameters); - return new ObjectResult(result); - } + return new ObjectResult(result); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentSource.cs index ebcb38dcb46..b31c897670d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentSource.cs @@ -5,37 +5,36 @@ using OrchardCore.Deployment; using OrchardCore.Json; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +public class AllQueriesDeploymentSource : IDeploymentSource { - public class AllQueriesDeploymentSource : IDeploymentSource + private readonly IQueryManager _queryManager; + private readonly JsonSerializerOptions _jsonSerializerOptions; + + public AllQueriesDeploymentSource( + IQueryManager queryManager, + IOptions jsonSerializerOptions) { - private readonly IQueryManager _queryManager; - private readonly JsonSerializerOptions _jsonSerializerOptions; + _queryManager = queryManager; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + } - public AllQueriesDeploymentSource( - IQueryManager queryManager, - IOptions jsonSerializerOptions) - { - _queryManager = queryManager; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var allQueriesStep = step as AllQueriesDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (allQueriesStep == null) { - var allQueriesStep = step as AllQueriesDeploymentStep; - - if (allQueriesStep == null) - { - return; - } + return; + } - var queries = await _queryManager.ListQueriesAsync(); + var queries = await _queryManager.ListQueriesAsync(); - result.Steps.Add(new JsonObject - { - ["name"] = "Queries", - ["Queries"] = JArray.FromObject(queries, _jsonSerializerOptions), - }); - } + result.Steps.Add(new JsonObject + { + ["name"] = "Queries", + ["Queries"] = JArray.FromObject(queries, _jsonSerializerOptions), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStep.cs index d8360abaa1f..5c47f5df4e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +/// +/// Adds all queries to a . +/// +public class AllQueriesDeploymentStep : DeploymentStep { - /// - /// Adds all queries to a . - /// - public class AllQueriesDeploymentStep : DeploymentStep + public AllQueriesDeploymentStep() { - public AllQueriesDeploymentStep() - { - Name = "AllQueries"; - } + Name = "AllQueries"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStepDriver.cs index 47e0159b732..2f549a70677 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/AllQueriesDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +public sealed class AllQueriesDeploymentStepDriver : DisplayDriver { - public sealed class AllQueriesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllQueriesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllQueriesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllQueriesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllQueriesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllQueriesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllQueriesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllQueriesDeploymentStep step, BuildEditorContext context) - { - return View("AllQueriesDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllQueriesDeploymentStep step, BuildEditorContext context) + { + return View("AllQueriesDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs index 1f2168b6e56..954dffd3eea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentSource.cs @@ -5,91 +5,90 @@ using OrchardCore.ContentManagement; using OrchardCore.Deployment; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +public class QueryBasedContentDeploymentSource : IDeploymentSource { - public class QueryBasedContentDeploymentSource : IDeploymentSource + private readonly IQueryManager _queryManager; + + public QueryBasedContentDeploymentSource(IQueryManager queryManager) + { + _queryManager = queryManager; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly IQueryManager _queryManager; + var queryDeploymentStep = step as QueryBasedContentDeploymentStep; - public QueryBasedContentDeploymentSource(IQueryManager queryManager) + if (queryDeploymentStep == null) { - _queryManager = queryManager; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - var queryDeploymentStep = step as QueryBasedContentDeploymentStep; + var data = new JsonArray(); - if (queryDeploymentStep == null) - { - return; - } + var query = await _queryManager.GetQueryAsync(queryDeploymentStep.QueryName); - var data = new JsonArray(); + if (query == null) + { + return; + } - var query = await _queryManager.GetQueryAsync(queryDeploymentStep.QueryName); + if (!query.CanReturnContentItems || !query.ReturnContentItems) + { + return; + } - if (query == null) - { - return; - } + if (!TryDeserializeParameters(queryDeploymentStep.QueryParameters ?? "{ }", out var parameters)) + { + return; + } - if (!query.CanReturnContentItems || !query.ReturnContentItems) - { - return; - } + var results = await _queryManager.ExecuteQueryAsync(query, parameters); - if (!TryDeserializeParameters(queryDeploymentStep.QueryParameters ?? "{ }", out var parameters)) - { - return; - } + foreach (var contentItem in results.Items) + { + var objectData = JObject.FromObject(contentItem); - var results = await _queryManager.ExecuteQueryAsync(query, parameters); + // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql. + objectData.Remove(nameof(ContentItem.Id)); - foreach (var contentItem in results.Items) + if (queryDeploymentStep.ExportAsSetupRecipe) { - var objectData = JObject.FromObject(contentItem); - - // Don't serialize the Id as it could be interpreted as an updated object when added back to YesSql. - objectData.Remove(nameof(ContentItem.Id)); - - if (queryDeploymentStep.ExportAsSetupRecipe) - { - objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; - objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; - objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; - objectData.Remove(nameof(ContentItem.ContentItemVersionId)); - objectData.Remove(nameof(ContentItem.CreatedUtc)); - objectData.Remove(nameof(ContentItem.ModifiedUtc)); - objectData.Remove(nameof(ContentItem.PublishedUtc)); - } - data.Add(objectData); + objectData[nameof(ContentItem.Owner)] = "[js: parameters('AdminUserId')]"; + objectData[nameof(ContentItem.Author)] = "[js: parameters('AdminUsername')]"; + objectData[nameof(ContentItem.ContentItemId)] = "[js: uuid()]"; + objectData.Remove(nameof(ContentItem.ContentItemVersionId)); + objectData.Remove(nameof(ContentItem.CreatedUtc)); + objectData.Remove(nameof(ContentItem.ModifiedUtc)); + objectData.Remove(nameof(ContentItem.PublishedUtc)); } + data.Add(objectData); + } - if (data.HasValues()) + if (data.HasValues()) + { + var jObj = new JsonObject { - var jObj = new JsonObject - { - ["name"] = "content", - ["data"] = data - }; + ["name"] = "content", + ["data"] = data + }; - result.Steps.Add(jObj); - } + result.Steps.Add(jObj); } + } - private static bool TryDeserializeParameters(string parameters, out Dictionary queryParameters) + private static bool TryDeserializeParameters(string parameters, out Dictionary queryParameters) + { + try { - try - { - queryParameters = JConvert.DeserializeObject>(parameters) ?? []; - return true; - } - catch (JsonException) - { - queryParameters = []; - return false; - } + queryParameters = JConvert.DeserializeObject>(parameters) ?? []; + return true; + } + catch (JsonException) + { + queryParameters = []; + return false; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStep.cs index de2085517e3..ac1157e5f67 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStep.cs @@ -1,21 +1,20 @@ using System.ComponentModel.DataAnnotations; using OrchardCore.Deployment; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +/// +/// Adds all content items from the result of a Query to a . +/// +public class QueryBasedContentDeploymentStep : DeploymentStep { - /// - /// Adds all content items from the result of a Query to a . - /// - public class QueryBasedContentDeploymentStep : DeploymentStep + public QueryBasedContentDeploymentStep() { - public QueryBasedContentDeploymentStep() - { - Name = "QueryBasedContentDeploymentStep"; - } - - [Required] - public string QueryName { get; set; } - public string QueryParameters { get; set; } - public bool ExportAsSetupRecipe { get; set; } + Name = "QueryBasedContentDeploymentStep"; } + + [Required] + public string QueryName { get; set; } + public string QueryParameters { get; set; } + public bool ExportAsSetupRecipe { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs index b733e8c4f5a..029ce6eb3d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Deployment/QueryBasedContentDeploymentStepDriver.cs @@ -8,78 +8,77 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Queries.ViewModels; -namespace OrchardCore.Queries.Deployment +namespace OrchardCore.Queries.Deployment; + +public sealed class QueryBasedContentDeploymentStepDriver : DisplayDriver { - public sealed class QueryBasedContentDeploymentStepDriver : DisplayDriver + private readonly IQueryManager _queryManager; + + internal readonly IStringLocalizer S; + + public QueryBasedContentDeploymentStepDriver( + IQueryManager queryManager, + IStringLocalizer stringLocalizer) { - private readonly IQueryManager _queryManager; + _queryManager = queryManager; + S = stringLocalizer; + } - internal readonly IStringLocalizer S; + public override Task DisplayAsync(QueryBasedContentDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("QueryBasedContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("QueryBasedContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public QueryBasedContentDeploymentStepDriver( - IQueryManager queryManager, - IStringLocalizer stringLocalizer) + public override IDisplayResult Edit(QueryBasedContentDeploymentStep step, BuildEditorContext context) + { + return Initialize("QueryBasedContentDeploymentStep_Fields_Edit", async model => { - _queryManager = queryManager; - S = stringLocalizer; - } + model.QueryName = step.QueryName; + model.QueryParameters = step.QueryParameters; + model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; + model.Queries = await _queryManager.ListQueriesAsync(true); + }).Location("Content"); + } - public override Task DisplayAsync(QueryBasedContentDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("QueryBasedContentDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("QueryBasedContentDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + public override async Task UpdateAsync(QueryBasedContentDeploymentStep step, UpdateEditorContext context) + { + var queryBasedContentViewModel = new QueryBasedContentDeploymentStepViewModel(); + await context.Updater.TryUpdateModelAsync(queryBasedContentViewModel, Prefix, + viewModel => viewModel.QueryName, + viewModel => viewModel.QueryParameters, + viewModel => viewModel.ExportAsSetupRecipe); + + var query = await _queryManager.GetQueryAsync(queryBasedContentViewModel.QueryName); - public override IDisplayResult Edit(QueryBasedContentDeploymentStep step, BuildEditorContext context) + if (!query.CanReturnContentItems || !query.ReturnContentItems) { - return Initialize("QueryBasedContentDeploymentStep_Fields_Edit", async model => - { - model.QueryName = step.QueryName; - model.QueryParameters = step.QueryParameters; - model.ExportAsSetupRecipe = step.ExportAsSetupRecipe; - model.Queries = await _queryManager.ListQueriesAsync(true); - }).Location("Content"); + context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryName), S["Your Query is not returning content items."]); } - public override async Task UpdateAsync(QueryBasedContentDeploymentStep step, UpdateEditorContext context) + if (queryBasedContentViewModel.QueryParameters != null) { - var queryBasedContentViewModel = new QueryBasedContentDeploymentStepViewModel(); - await context.Updater.TryUpdateModelAsync(queryBasedContentViewModel, Prefix, - viewModel => viewModel.QueryName, - viewModel => viewModel.QueryParameters, - viewModel => viewModel.ExportAsSetupRecipe); - - var query = await _queryManager.GetQueryAsync(queryBasedContentViewModel.QueryName); - - if (!query.CanReturnContentItems || !query.ReturnContentItems) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryName), S["Your Query is not returning content items."]); - } - - if (queryBasedContentViewModel.QueryParameters != null) + try { - try + var parameters = JConvert.DeserializeObject>(queryBasedContentViewModel.QueryParameters); + if (parameters == null) { - var parameters = JConvert.DeserializeObject>(queryBasedContentViewModel.QueryParameters); - if (parameters == null) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryParameters), S["Make sure it is a valid JSON object. Example: { key : 'value' }"]); - } - } - catch (JsonException) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryParameters), S["Something is wrong with your JSON."]); + context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryParameters), S["Make sure it is a valid JSON object. Example: { key : 'value' }"]); } } + catch (JsonException) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(step.QueryParameters), S["Something is wrong with your JSON."]); + } + } - step.QueryName = queryBasedContentViewModel.QueryName; - step.ExportAsSetupRecipe = queryBasedContentViewModel.ExportAsSetupRecipe; - step.QueryParameters = queryBasedContentViewModel.QueryParameters; + step.QueryName = queryBasedContentViewModel.QueryName; + step.ExportAsSetupRecipe = queryBasedContentViewModel.ExportAsSetupRecipe; + step.QueryParameters = queryBasedContentViewModel.QueryParameters; - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs index 27fbcee13e4..0c6fc5a96ef 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Drivers/QueryDisplayDriver.cs @@ -6,94 +6,93 @@ using OrchardCore.Mvc.Utilities; using OrchardCore.Queries.ViewModels; -namespace OrchardCore.Queries.Drivers +namespace OrchardCore.Queries.Drivers; + +public sealed class QueryDisplayDriver : DisplayDriver { - public sealed class QueryDisplayDriver : DisplayDriver + private readonly IQueryManager _queryManager; + + internal readonly IStringLocalizer S; + + public QueryDisplayDriver( + IQueryManager queryManager, + IStringLocalizer stringLocalizer) { - private readonly IQueryManager _queryManager; + _queryManager = queryManager; + S = stringLocalizer; + } + + public override Task DisplayAsync(Query query, BuildDisplayContext context) + { + return CombineAsync( + Dynamic("Query_Fields_SummaryAdmin", model => + { + model.Name = query.Name; + model.Source = query.Source; + model.Schema = query.Schema; + model.Query = query; + }).Location("Content:1"), + Dynamic("Query_Buttons_SummaryAdmin", model => + { + model.Name = query.Name; + model.Source = query.Source; + model.Schema = query.Schema; + model.Query = query; + }).Location("Actions:5") + ); + } - internal readonly IStringLocalizer S; + public override Task EditAsync(Query query, BuildEditorContext context) + { + return CombineAsync( + Initialize("Query_Fields_Edit", model => + { + model.Name = query.Name; + model.Source = query.Source; + model.Schema = query.Schema; + model.Query = query; + }).Location("Content:1"), + Initialize("Query_Fields_Buttons", model => + { + model.Name = query.Name; + model.Source = query.Source; + model.Schema = query.Schema; + model.Query = query; + }).Location("Actions:5") + ); + } - public QueryDisplayDriver( - IQueryManager queryManager, - IStringLocalizer stringLocalizer) + public override async Task UpdateAsync(Query model, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.Name, + m => m.Source, + m => m.Schema); + + if (string.IsNullOrEmpty(model.Name)) { - _queryManager = queryManager; - S = stringLocalizer; + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name is required"]); } - public override Task DisplayAsync(Query query, BuildDisplayContext context) + if (!string.IsNullOrEmpty(model.Schema) && !model.Schema.IsJson()) { - return CombineAsync( - Dynamic("Query_Fields_SummaryAdmin", model => - { - model.Name = query.Name; - model.Source = query.Source; - model.Schema = query.Schema; - model.Query = query; - }).Location("Content:1"), - Dynamic("Query_Buttons_SummaryAdmin", model => - { - model.Name = query.Name; - model.Source = query.Source; - model.Schema = query.Schema; - model.Query = query; - }).Location("Actions:5") - ); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Schema), S["Invalid schema JSON supplied."]); } - - public override Task EditAsync(Query query, BuildEditorContext context) + var safeName = model.Name.ToSafeName(); + if (string.IsNullOrEmpty(safeName) || model.Name != safeName) { - return CombineAsync( - Initialize("Query_Fields_Edit", model => - { - model.Name = query.Name; - model.Source = query.Source; - model.Schema = query.Schema; - model.Query = query; - }).Location("Content:1"), - Initialize("Query_Fields_Buttons", model => - { - model.Name = query.Name; - model.Source = query.Source; - model.Schema = query.Schema; - model.Query = query; - }).Location("Actions:5") - ); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name contains illegal characters"]); } - - public override async Task UpdateAsync(Query model, UpdateEditorContext context) + else { - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.Name, - m => m.Source, - m => m.Schema); + var existing = await _queryManager.GetQueryAsync(safeName); - if (string.IsNullOrEmpty(model.Name)) + if (existing != null && existing != model) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name is required"]); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["A query with the same name already exists"]); } - - if (!string.IsNullOrEmpty(model.Schema) && !model.Schema.IsJson()) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Schema), S["Invalid schema JSON supplied."]); - } - var safeName = model.Name.ToSafeName(); - if (string.IsNullOrEmpty(safeName) || model.Name != safeName) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["Name contains illegal characters"]); - } - else - { - var existing = await _queryManager.GetQueryAsync(safeName); - - if (existing != null && existing != model) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Name), S["A query with the same name already exists"]); - } - } - - return await EditAsync(model, context); } + + return await EditAsync(model, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/LiquidQueriesAccessor.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/LiquidQueriesAccessor.cs index adc2334b6cc..237d103f626 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/LiquidQueriesAccessor.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/LiquidQueriesAccessor.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +/// +/// This is a placeholder class that allows modules to extend the `Queries` property in the current Liquid scope. +/// +public class LiquidQueriesAccessor { - /// - /// This is a placeholder class that allows modules to extend the `Queries` property in the current Liquid scope. - /// - public class LiquidQueriesAccessor - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs index ce52bdf98d3..9e27407b2d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Liquid/QueryFilter.cs @@ -6,38 +6,37 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.Liquid; -namespace OrchardCore.Queries.Liquid +namespace OrchardCore.Queries.Liquid; + +public class QueryFilter : ILiquidFilter { - public class QueryFilter : ILiquidFilter + private readonly IServiceProvider _serviceProvider; + + public QueryFilter(IServiceProvider serviceProvider) { - private readonly IServiceProvider _serviceProvider; + _serviceProvider = serviceProvider; + } - public QueryFilter(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + var query = input.ToObjectValue() as Query; - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + if (query == null) { - var query = input.ToObjectValue() as Query; - - if (query == null) - { - return NilValue.Instance; - } + return NilValue.Instance; + } - var parameters = new Dictionary(); + var parameters = new Dictionary(); - foreach (var name in arguments.Names) - { - parameters.Add(name, arguments[name].ToObjectValue()); - } + foreach (var name in arguments.Names) + { + parameters.Add(name, arguments[name].ToObjectValue()); + } - var queryManager = _serviceProvider.GetRequiredService(); + var queryManager = _serviceProvider.GetRequiredService(); - var result = await queryManager.ExecuteQueryAsync(query, parameters); + var result = await queryManager.ExecuteQueryAsync(query, parameters); - return FluidValue.Create(result.Items, ctx.Options); - } + return FluidValue.Create(result.Items, ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs b/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs index 467138317d0..985fb708446 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/QueryGlobalMethodProvider.cs @@ -3,41 +3,40 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.Scripting; -namespace OrchardCore.Queries +namespace OrchardCore.Queries; + +public class QueryGlobalMethodProvider : IGlobalMethodProvider { - public class QueryGlobalMethodProvider : IGlobalMethodProvider - { - private readonly GlobalMethod _executeQuery; + private readonly GlobalMethod _executeQuery; - /// - /// Usage: executeQuery(name, parameters) - /// Ex: executeQuery("MySqlQuery", {"Owner":"bob"});. - /// - public QueryGlobalMethodProvider() + /// + /// Usage: executeQuery(name, parameters) + /// Ex: executeQuery("MySqlQuery", {"Owner":"bob"});. + /// + public QueryGlobalMethodProvider() + { + _executeQuery = new GlobalMethod { - _executeQuery = new GlobalMethod + Name = "executeQuery", + Method = serviceProvider => (Func)((name, parameters) => { - Name = "executeQuery", - Method = serviceProvider => (Func)((name, parameters) => - { - var queryManager = serviceProvider.GetRequiredService(); - var query = queryManager.GetQueryAsync(name).GetAwaiter().GetResult(); + var queryManager = serviceProvider.GetRequiredService(); + var query = queryManager.GetQueryAsync(name).GetAwaiter().GetResult(); - if (query == null) - { - return null; - } + if (query == null) + { + return null; + } - var result = queryManager.ExecuteQueryAsync(query, (IDictionary)parameters).GetAwaiter().GetResult(); + var result = queryManager.ExecuteQueryAsync(query, (IDictionary)parameters).GetAwaiter().GetResult(); - return result.Items; - }), - }; - } + return result.Items; + }), + }; + } - public IEnumerable GetMethods() - { - return [_executeQuery]; - } + public IEnumerable GetMethods() + { + return [_executeQuery]; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs index 4bd19af3493..d24365d586f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Recipes/QueryStep.cs @@ -11,89 +11,88 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Queries.Recipes +namespace OrchardCore.Queries.Recipes; + +/// +/// This recipe step creates a set of queries. +/// +public sealed class QueryStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of queries. - /// - public sealed class QueryStep : IRecipeStepHandler - { - private readonly IQueryManager _queryManager; - private readonly JsonSerializerOptions _jsonSerializerOptions; - private readonly ILogger _logger; + private readonly IQueryManager _queryManager; + private readonly JsonSerializerOptions _jsonSerializerOptions; + private readonly ILogger _logger; + + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public QueryStep( + IQueryManager queryManager, + IOptions jsonSerializerOptions, + ILogger logger, + IStringLocalizer stringLocalizer) + { + _queryManager = queryManager; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + _logger = logger; + S = stringLocalizer; + } - public QueryStep( - IQueryManager queryManager, - IOptions jsonSerializerOptions, - ILogger logger, - IStringLocalizer stringLocalizer) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Queries", StringComparison.OrdinalIgnoreCase)) { - _queryManager = queryManager; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; - _logger = logger; - S = stringLocalizer; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "Queries", StringComparison.OrdinalIgnoreCase)) - { - return; - } + var model = context.Step.ToObject(_jsonSerializerOptions); - var model = context.Step.ToObject(_jsonSerializerOptions); + var queries = new List(); - var queries = new List(); + foreach (var token in model.Queries.Cast()) + { + var name = token[nameof(Query.Name)]?.GetValue(); - foreach (var token in model.Queries.Cast()) + if (string.IsNullOrEmpty(name)) { - var name = token[nameof(Query.Name)]?.GetValue(); + context.Errors.Add(S["Query name is missing or empty. The query will not be imported."]); - if (string.IsNullOrEmpty(name)) - { - context.Errors.Add(S["Query name is missing or empty. The query will not be imported."]); + continue; + } - continue; - } + var sourceName = token[nameof(Query.Source)]?.GetValue(); - var sourceName = token[nameof(Query.Source)]?.GetValue(); + if (string.IsNullOrEmpty(sourceName)) + { + context.Errors.Add(S["Could not find query source value. The query '{0}' will not be imported.", name]); - if (string.IsNullOrEmpty(sourceName)) - { - context.Errors.Add(S["Could not find query source value. The query '{0}' will not be imported.", name]); + continue; + } - continue; - } + var query = await _queryManager.GetQueryAsync(name); - var query = await _queryManager.GetQueryAsync(name); + if (query == null) + { + query = await _queryManager.NewAsync(sourceName, token); if (query == null) { - query = await _queryManager.NewAsync(sourceName, token); - - if (query == null) - { - context.Errors.Add(S["Could not find query source: '{0}'. The query '{1}' will not be imported.", sourceName, name]); - - continue; - } + context.Errors.Add(S["Could not find query source: '{0}'. The query '{1}' will not be imported.", sourceName, name]); - queries.Add(query); - } - else - { - await _queryManager.UpdateAsync(query, token); + continue; } - } - await _queryManager.SaveAsync(queries.ToArray()); + queries.Add(query); + } + else + { + await _queryManager.UpdateAsync(query, token); + } } - } - public sealed class QueryStepModel - { - public JsonArray Queries { get; set; } + await _queryManager.SaveAsync(queries.ToArray()); } } + +public sealed class QueryStepModel +{ + public JsonArray Queries { get; set; } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/AdminMenu.cs index 1efdb1e6033..bfd9bb36425 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/AdminMenu.cs @@ -2,36 +2,35 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Search"], search => search - .Add(S["Queries"], S["Queries"].PrefixPosition(), queries => queries - .Add(S["Run SQL Query"], S["Run SQL Query"].PrefixPosition(), sql => sql - .Action("Query", "Admin", "OrchardCore.Queries") - .Permission(Permissions.ManageSqlQueries) - .LocalNav() - ) + builder + .Add(S["Search"], search => search + .Add(S["Queries"], S["Queries"].PrefixPosition(), queries => queries + .Add(S["Run SQL Query"], S["Run SQL Query"].PrefixPosition(), sql => sql + .Action("Query", "Admin", "OrchardCore.Queries") + .Permission(Permissions.ManageSqlQueries) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Controllers/AdminController.cs index e725dd18a30..eb50a9409d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Controllers/AdminController.cs @@ -17,99 +17,98 @@ using OrchardCore.Queries.Sql.ViewModels; using YesSql; -namespace OrchardCore.Queries.Sql.Controllers +namespace OrchardCore.Queries.Sql.Controllers; + +[Feature("OrchardCore.Queries.Sql")] +public class AdminController : Controller { - [Feature("OrchardCore.Queries.Sql")] - public class AdminController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IStore _store; + private readonly ILiquidTemplateManager _liquidTemplateManager; + protected readonly IStringLocalizer S; + private readonly TemplateOptions _templateOptions; + + public AdminController( + IAuthorizationService authorizationService, + IStore store, + ILiquidTemplateManager liquidTemplateManager, + IStringLocalizer stringLocalizer, + IOptions templateOptions) + { - private readonly IAuthorizationService _authorizationService; - private readonly IStore _store; - private readonly ILiquidTemplateManager _liquidTemplateManager; - protected readonly IStringLocalizer S; - private readonly TemplateOptions _templateOptions; - - public AdminController( - IAuthorizationService authorizationService, - IStore store, - ILiquidTemplateManager liquidTemplateManager, - IStringLocalizer stringLocalizer, - IOptions templateOptions) + _authorizationService = authorizationService; + _store = store; + _liquidTemplateManager = liquidTemplateManager; + S = stringLocalizer; + _templateOptions = templateOptions.Value; + } + [Admin("Queries/Sql/Query", "QueriesRunSql")] + public Task Query(string query) + { + query = string.IsNullOrWhiteSpace(query) ? "" : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)); + return Query(new AdminQueryViewModel { - _authorizationService = authorizationService; - _store = store; - _liquidTemplateManager = liquidTemplateManager; - S = stringLocalizer; - _templateOptions = templateOptions.Value; - } + DecodedQuery = query, + FactoryName = _store.Configuration.ConnectionFactory.GetType().FullName + }); + } + + [HttpPost] + public async Task Query(AdminQueryViewModel model) + { + model.FactoryName = _store.Configuration.ConnectionFactory.GetType().FullName; - [Admin("Queries/Sql/Query", "QueriesRunSql")] - public Task Query(string query) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSqlQueries)) { - query = string.IsNullOrWhiteSpace(query) ? "" : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)); - return Query(new AdminQueryViewModel - { - DecodedQuery = query, - FactoryName = _store.Configuration.ConnectionFactory.GetType().FullName - }); + return Forbid(); } - [HttpPost] - public async Task Query(AdminQueryViewModel model) + if (string.IsNullOrWhiteSpace(model.DecodedQuery)) { - model.FactoryName = _store.Configuration.ConnectionFactory.GetType().FullName; - - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSqlQueries)) - { - return Forbid(); - } + return View(model); + } - if (string.IsNullOrWhiteSpace(model.DecodedQuery)) - { - return View(model); - } + if (string.IsNullOrEmpty(model.Parameters)) + { + model.Parameters = "{ }"; + } - if (string.IsNullOrEmpty(model.Parameters)) - { - model.Parameters = "{ }"; - } + var stopwatch = new Stopwatch(); + stopwatch.Start(); - var stopwatch = new Stopwatch(); - stopwatch.Start(); + var dialect = _store.Configuration.SqlDialect; - var dialect = _store.Configuration.SqlDialect; + var parameters = JConvert.DeserializeObject>(model.Parameters); - var parameters = JConvert.DeserializeObject>(model.Parameters); + var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, NullEncoder.Default, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); - var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, NullEncoder.Default, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); + if (SqlParser.TryParse(tokenizedQuery, _store.Configuration.Schema, dialect, _store.Configuration.TablePrefix, parameters, out var rawQuery, out var messages)) + { + model.RawSql = rawQuery; + model.Parameters = JConvert.SerializeObject(parameters, JOptions.Indented); - if (SqlParser.TryParse(tokenizedQuery, _store.Configuration.Schema, dialect, _store.Configuration.TablePrefix, parameters, out var rawQuery, out var messages)) + try { - model.RawSql = rawQuery; - model.Parameters = JConvert.SerializeObject(parameters, JOptions.Indented); - - try - { - await using var connection = _store.Configuration.ConnectionFactory.CreateConnection(); - await connection.OpenAsync(); - model.Documents = await connection.QueryAsync(rawQuery, parameters); - } - catch (Exception e) - { - ModelState.AddModelError("", S["An error occurred while executing the SQL query: {0}", e.Message]); - } + await using var connection = _store.Configuration.ConnectionFactory.CreateConnection(); + await connection.OpenAsync(); + model.Documents = await connection.QueryAsync(rawQuery, parameters); } - else + catch (Exception e) { - foreach (var message in messages) - { - ModelState.AddModelError("", message); - } + ModelState.AddModelError("", S["An error occurred while executing the SQL query: {0}", e.Message]); } + } + else + { + foreach (var message in messages) + { + ModelState.AddModelError("", message); + } + } - model.Elapsed = stopwatch.Elapsed; + model.Elapsed = stopwatch.Elapsed; - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs index 2a1b0ccd6a4..44cd77c4adb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Drivers/SqlQueryDisplayDriver.cs @@ -7,82 +7,81 @@ using OrchardCore.Queries.Sql.Models; using OrchardCore.Queries.Sql.ViewModels; -namespace OrchardCore.Queries.Sql.Drivers +namespace OrchardCore.Queries.Sql.Drivers; + +public sealed class SqlQueryDisplayDriver : DisplayDriver { - public sealed class SqlQueryDisplayDriver : DisplayDriver + internal readonly IStringLocalizer S; + + public SqlQueryDisplayDriver(IStringLocalizer stringLocalizer) { - internal readonly IStringLocalizer S; + S = stringLocalizer; + } - public SqlQueryDisplayDriver(IStringLocalizer stringLocalizer) + public override IDisplayResult Display(Query query, BuildDisplayContext context) + { + if (query.Source != SqlQuerySource.SourceName) { - S = stringLocalizer; + return null; } - public override IDisplayResult Display(Query query, BuildDisplayContext context) - { - if (query.Source != SqlQuerySource.SourceName) + return Combine( + Dynamic("SqlQuery_SummaryAdmin", model => { - return null; - } + model.Query = query; + }).Location("Content:5"), + Dynamic("SqlQuery_Buttons_SummaryAdmin", model => + { + model.Query = query; + }).Location("Actions:2") + ); + } - return Combine( - Dynamic("SqlQuery_SummaryAdmin", model => - { - model.Query = query; - }).Location("Content:5"), - Dynamic("SqlQuery_Buttons_SummaryAdmin", model => - { - model.Query = query; - }).Location("Actions:2") - ); + public override IDisplayResult Edit(Query query, BuildEditorContext context) + { + if (query.Source != SqlQuerySource.SourceName) + { + return null; } - public override IDisplayResult Edit(Query query, BuildEditorContext context) + return Initialize("SqlQuery_Edit", async model => { - if (query.Source != SqlQuerySource.SourceName) + model.ReturnDocuments = query.ReturnContentItems; + + var metadata = query.As(); + model.Query = metadata.Template; + + // Extract query from the query string if we come from the main query editor. + if (string.IsNullOrEmpty(metadata.Template)) { - return null; + await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); } + }).Location("Content:5"); + } - return Initialize("SqlQuery_Edit", async model => - { - model.ReturnDocuments = query.ReturnContentItems; + public override async Task UpdateAsync(Query query, UpdateEditorContext context) + { + if (query.Source != SqlQuerySource.SourceName) + { + return null; + } - var metadata = query.As(); - model.Query = metadata.Template; + var viewModel = new SqlQueryViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, + m => m.Query, + m => m.ReturnDocuments); - // Extract query from the query string if we come from the main query editor. - if (string.IsNullOrEmpty(metadata.Template)) - { - await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); - } - }).Location("Content:5"); + if (string.IsNullOrWhiteSpace(viewModel.Query)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); } - public override async Task UpdateAsync(Query query, UpdateEditorContext context) + query.ReturnContentItems = viewModel.ReturnDocuments; + query.Put(new SqlQueryMetadata() { - if (query.Source != SqlQuerySource.SourceName) - { - return null; - } - - var viewModel = new SqlQueryViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, - m => m.Query, - m => m.ReturnDocuments); + Template = viewModel.Query, + }); - if (string.IsNullOrWhiteSpace(viewModel.Query)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); - } - - query.ReturnContentItems = viewModel.ReturnDocuments; - query.Put(new SqlQueryMetadata() - { - Template = viewModel.Query, - }); - - return Edit(query, context); - } + return Edit(query, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs index 1df672f97d8..3bf7234b706 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/SqlQueryFieldTypeProvider.cs @@ -16,210 +16,209 @@ using OrchardCore.Entities; using OrchardCore.Queries.Sql.Models; -namespace OrchardCore.Queries.Sql.GraphQL.Queries +namespace OrchardCore.Queries.Sql.GraphQL.Queries; + +/// +/// This implementation of registers +/// all SQL Queries as GraphQL queries. +/// +public class SqlQueryFieldTypeProvider : ISchemaBuilder { - /// - /// This implementation of registers - /// all SQL Queries as GraphQL queries. - /// - public class SqlQueryFieldTypeProvider : ISchemaBuilder + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public SqlQueryFieldTypeProvider( + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } - public SqlQueryFieldTypeProvider( - IHttpContextAccessor httpContextAccessor, - ILogger logger) - { - _httpContextAccessor = httpContextAccessor; - _logger = logger; - } + public Task GetIdentifierAsync() + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public Task GetIdentifierAsync() - { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + return queryManager.GetIdentifierAsync(); + } - return queryManager.GetIdentifierAsync(); - } + public async Task BuildAsync(ISchema schema) + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public async Task BuildAsync(ISchema schema) + var queries = await queryManager.ListQueriesBySourceAsync(SqlQuerySource.SourceName); + + foreach (var query in queries) { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + if (string.IsNullOrWhiteSpace(query.Schema)) + { + continue; + } - var queries = await queryManager.ListQueriesBySourceAsync(SqlQuerySource.SourceName); + var name = query.Name; - foreach (var query in queries) + try { - if (string.IsNullOrWhiteSpace(query.Schema)) + var querySchema = JObject.Parse(query.Schema); + if (!querySchema.ContainsKey("type")) { + _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", name); continue; } + var type = querySchema["type"].ToString(); + FieldType fieldType; - var name = query.Name; - - try - { - var querySchema = JObject.Parse(query.Schema); - if (!querySchema.ContainsKey("type")) - { - _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", name); - continue; - } - var type = querySchema["type"].ToString(); - FieldType fieldType; + var metadata = query.As(); - var metadata = query.As(); + var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; - var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; - - if (query.ReturnContentItems && type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) - { - var contentType = type.Remove(0, 12); - fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); - } - else - { - fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); - } - - if (fieldType != null) - { - schema.Query.AddField(fieldType); - } + if (query.ReturnContentItems && type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) + { + var contentType = type.Remove(0, 12); + fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); } - catch (Exception e) + else { - _logger.LogError(e, "The Query '{Name}' has an invalid schema.", name); + fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); } + + if (fieldType != null) + { + schema.Query.AddField(fieldType); + } + } + catch (Exception e) + { + _logger.LogError(e, "The Query '{Name}' has an invalid schema.", name); } } + } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + { + var properties = querySchema["properties"]?.AsObject(); + if (properties == null) { - var properties = querySchema["properties"]?.AsObject(); - if (properties == null) - { - return null; - } + return null; + } - var typeType = new ObjectGraphType - { - Name = fieldTypeName - }; + var typeType = new ObjectGraphType + { + Name = fieldTypeName + }; - foreach (var child in properties) - { - var name = child.Key; - var nameLower = name.Replace('.', '_'); - var type = child.Value["type"].ToString(); - var description = child.Value["description"]?.ToString(); + foreach (var child in properties) + { + var name = child.Key; + var nameLower = name.Replace('.', '_'); + var type = child.Value["type"].ToString(); + var description = child.Value["description"]?.ToString(); - if (type == "string") + if (type == "string") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(StringGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(StringGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - field.Metadata.Add("Name", name); - typeType.AddField(field); - } - else if (type == "integer") + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; + field.Metadata.Add("Name", name); + typeType.AddField(field); + } + else if (type == "integer") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(IntGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(IntGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - - field.Metadata.Add("Name", name); - typeType.AddField(field); - } - } + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = new ListGraphType(typeType), - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeof(ListGraphType>) - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + field.Metadata.Add("Name", name); + typeType.AddField(field); + } + } - var iQuery = await queryManager.GetQueryAsync(query.Name); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = new ListGraphType(typeType), + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeof(ListGraphType>) + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var parameters = context.GetArgument("parameters"); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var parameters = context.GetArgument("parameters"); - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - return result.Items; - } + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return fieldType; + return result.Items; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) - { - var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); - if (typeType == null) - { - return null; - } + return fieldType; + } - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = typeType.ResolvedType, - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeType.Type - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + { + var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); + if (typeType == null) + { + return null; + } - var iQuery = await queryManager.GetQueryAsync(query.Name); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = typeType.ResolvedType, + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeType.Type + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var parameters = context.GetArgument("parameters"); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var parameters = context.GetArgument("parameters"); - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - return result.Items; - } + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return fieldType; + return result.Items; } + + return fieldType; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/Startup.cs index 21c3f37abbf..e2e09ab3c2f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/GraphQL/Startup.cs @@ -3,18 +3,17 @@ using OrchardCore.Modules; using OrchardCore.Queries.Sql.GraphQL.Queries; -namespace OrchardCore.Queries.Sql.GraphQL +namespace OrchardCore.Queries.Sql.GraphQL; + +/// +/// These services are registered on the tenant service collection. +/// +[Feature("OrchardCore.Queries.Sql")] +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - [Feature("OrchardCore.Queries.Sql")] - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs index eb48ec77bae..319b7a8d4b6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlGrammar.cs @@ -1,225 +1,224 @@ using Irony.Parsing; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +public class SqlGrammar : Grammar { - public class SqlGrammar : Grammar + public SqlGrammar() : base(false) { - public SqlGrammar() : base(false) - { - var comment = new CommentTerminal("comment", "/*", "*/"); - var lineComment = new CommentTerminal("line_comment", "--", "\n", "\r\n"); - NonGrammarTerminals.Add(comment); - NonGrammarTerminals.Add(lineComment); - var number = new NumberLiteral("number"); - var string_literal = new StringLiteral("string", "'", StringOptions.AllowsDoubledQuote); - var Id_simple = TerminalFactory.CreateSqlExtIdentifier(this, "id_simple"); // covers normal identifiers (abc) and quoted id's ([abc d], "abc d") - var comma = ToTerm(","); - var dot = ToTerm("."); + var comment = new CommentTerminal("comment", "/*", "*/"); + var lineComment = new CommentTerminal("line_comment", "--", "\n", "\r\n"); + NonGrammarTerminals.Add(comment); + NonGrammarTerminals.Add(lineComment); + var number = new NumberLiteral("number"); + var string_literal = new StringLiteral("string", "'", StringOptions.AllowsDoubledQuote); + var Id_simple = TerminalFactory.CreateSqlExtIdentifier(this, "id_simple"); // covers normal identifiers (abc) and quoted id's ([abc d], "abc d") + var comma = ToTerm(","); + var dot = ToTerm("."); #pragma warning disable IDE0059 // Unnecessary assignment of a value - var CREATE = ToTerm("CREATE"); - var NULL = ToTerm("NULL"); + var CREATE = ToTerm("CREATE"); + var NULL = ToTerm("NULL"); #pragma warning restore IDE0059 // Unnecessary assignment of a value - var NOT = ToTerm("NOT"); - var ON = ToTerm("ON"); - var SELECT = ToTerm("SELECT"); - var FROM = ToTerm("FROM"); - var AS = ToTerm("AS"); + var NOT = ToTerm("NOT"); + var ON = ToTerm("ON"); + var SELECT = ToTerm("SELECT"); + var FROM = ToTerm("FROM"); + var AS = ToTerm("AS"); #pragma warning disable IDE0059 // Unnecessary assignment of a value - var COUNT = ToTerm("COUNT"); + var COUNT = ToTerm("COUNT"); #pragma warning restore IDE0059 // Unnecessary assignment of a value - var JOIN = ToTerm("JOIN"); - var BY = ToTerm("BY"); - var TRUE = ToTerm("TRUE"); - var FALSE = ToTerm("FALSE"); - var AND = ToTerm("AND"); - var OVER = ToTerm("OVER"); - var UNION = ToTerm("UNION"); - var ALL = ToTerm("ALL"); - var WITH = ToTerm("WITH"); - var CTE = TerminalFactory.CreateSqlExtIdentifier(this, "CTE"); - var ColumnAlias = TerminalFactory.CreateSqlExtIdentifier(this, "ColumnAlias"); - var TableAlias = TerminalFactory.CreateSqlExtIdentifier(this, "TableAlias"); - - // Non-terminals. - var Id = new NonTerminal("Id"); - var statement = new NonTerminal("stmt"); - var selectStatement = new NonTerminal("selectStatement"); - var idlist = new NonTerminal("idlist"); - var tableAliasList = new NonTerminal("tableAliasList"); - var tableAliasItem = new NonTerminal("tableAliasItem"); - var orderList = new NonTerminal("orderList"); - var orderMember = new NonTerminal("orderMember"); - var orderDirOptional = new NonTerminal("orderDirOpt"); - var whereClauseOptional = new NonTerminal("whereClauseOpt"); - var expression = new NonTerminal("expression"); - var expressionList = new NonTerminal("exprList"); - var optionalSelectRestriction = new NonTerminal("optionalSelectRestriction"); - var selectorList = new NonTerminal("selList"); - var fromClauseOpt = new NonTerminal("fromClauseOpt"); - var groupClauseOpt = new NonTerminal("groupClauseOpt"); - var havingClauseOpt = new NonTerminal("havingClauseOpt"); - var orderClauseOpt = new NonTerminal("orderClauseOpt"); - var limitClauseOpt = new NonTerminal("limitClauseOpt"); - var offsetClauseOpt = new NonTerminal("offsetClauseOpt"); - var columnItemList = new NonTerminal("columnItemList"); - var columnItem = new NonTerminal("columnItem"); - var columnSource = new NonTerminal("columnSource"); - var asOpt = new NonTerminal("asOpt"); - var tableAliasOpt = new NonTerminal("tableAliasOpt"); - var tuple = new NonTerminal("tuple"); - var joinChainOpt = new NonTerminal("joinChainOpt"); - var joinStatement = new NonTerminal("joinStatement"); - var joinKindOpt = new NonTerminal("joinKindOpt"); - var joinConditions = new NonTerminal("joinConditions"); - var joinCondition = new NonTerminal("joinCondition"); - var joinConditionArgument = new NonTerminal("joinConditionArgument"); - var term = new NonTerminal("term"); - var unExpr = new NonTerminal("unExpr"); - var unOp = new NonTerminal("unOp"); - var binExpr = new NonTerminal("binExpr"); - var binOp = new NonTerminal("binOp"); - var betweenExpr = new NonTerminal("betweenExpr"); - var inExpr = new NonTerminal("inExpr"); - var parSelectStatement = new NonTerminal("parSelectStmt"); - var notOpt = new NonTerminal("notOpt"); - var funCall = new NonTerminal("funCall"); - var parameter = new NonTerminal("parameter"); - var statementLine = new NonTerminal("stmtLine"); - var optionalSemicolon = new NonTerminal("semiOpt"); - var statementList = new NonTerminal("stmtList"); - var functionArguments = new NonTerminal("funArgs"); - var boolean = new NonTerminal("boolean"); - var overClauseOpt = new NonTerminal("overClauseOpt"); - var overArgumentsOpt = new NonTerminal("overArgumentsOpt"); - var overPartitionByClauseOpt = new NonTerminal("overPartitionByClauseOpt"); - var overOrderByClauseOpt = new NonTerminal("overOrderByClauseOpt"); - var unionStatementList = new NonTerminal("unionStmtList"); - var unionStatement = new NonTerminal("unionStmt"); - var unionClauseOpt = new NonTerminal("unionClauseOpt"); - var withClauseOpt = new NonTerminal("withClauseOpt"); - var cteList = new NonTerminal("cteList"); - var cte = new NonTerminal("cte"); - var cteColumnListOpt = new NonTerminal("cteColumnListOpt"); - var columnNames = new NonTerminal("columnNames"); - var IdColumn = new NonTerminal("IdColumn"); - var columnAliasOpt = new NonTerminal("columnAliasOpt"); - var IdTable = new NonTerminal("IdTable"); - var subQuery = new NonTerminal("subQuery"); - var tableAliasItemOrSubQuery = new NonTerminal("tableAliasItemOrSubQuery"); - var tableAliasOrSubQueryList = new NonTerminal("tableAliasOrSubQueryList"); - - // BNF Rules. - Root = statementList; - unionClauseOpt.Rule = Empty | UNION | UNION + ALL; - unionStatement.Rule = statement + unionClauseOpt; - unionStatementList.Rule = MakePlusRule(unionStatementList, unionStatement); - - statementLine.Rule = unionStatementList + optionalSemicolon; - optionalSemicolon.Rule = Empty | ";"; - statementList.Rule = MakePlusRule(statementList, statementLine); - - columnNames.Rule = MakePlusRule(columnNames, comma, Id_simple); - cteColumnListOpt.Rule = Empty | "(" + columnNames + ")"; - cte.Rule = CTE + cteColumnListOpt + AS + "(" + unionStatementList + ")"; - cteList.Rule = MakePlusRule(cteList, comma, cte); - withClauseOpt.Rule = Empty | WITH + cteList; - - statement.Rule = withClauseOpt + selectStatement; - - Id.Rule = MakePlusRule(Id, dot, Id_simple); - IdTable.Rule = MakePlusRule(IdTable, dot, TableAlias); - - tableAliasOpt.Rule = Empty | asOpt + IdTable; - IdColumn.Rule = MakePlusRule(IdColumn, dot, ColumnAlias); - columnAliasOpt.Rule = Empty | asOpt + IdColumn; - - asOpt.Rule = Empty | AS; - - idlist.Rule = MakePlusRule(idlist, comma, columnSource); - - tableAliasList.Rule = MakePlusRule(tableAliasList, comma, tableAliasItem); - tableAliasItem.Rule = Id + tableAliasOpt; - - subQuery.Rule = "(" + unionStatementList + ")" + AS + TableAlias; - tableAliasOrSubQueryList.Rule = MakePlusRule(tableAliasOrSubQueryList, comma, tableAliasItemOrSubQuery); - tableAliasItemOrSubQuery.Rule = tableAliasItem | subQuery; - - // Create Index. - orderList.Rule = MakePlusRule(orderList, comma, orderMember); - orderMember.Rule = Id + orderDirOptional; - orderDirOptional.Rule = Empty | "ASC" | "DESC"; - - // Select stmt. - selectStatement.Rule = SELECT + optionalSelectRestriction + selectorList + fromClauseOpt + whereClauseOptional + - groupClauseOpt + havingClauseOpt + orderClauseOpt + limitClauseOpt + offsetClauseOpt; - optionalSelectRestriction.Rule = Empty | "ALL" | "DISTINCT"; - selectorList.Rule = columnItemList | "*"; - columnItemList.Rule = MakePlusRule(columnItemList, comma, columnItem); - columnItem.Rule = columnSource + columnAliasOpt; - - columnSource.Rule = funCall + overClauseOpt | Id; - fromClauseOpt.Rule = Empty | FROM + tableAliasOrSubQueryList + joinChainOpt; - - joinChainOpt.Rule = MakeStarRule(joinChainOpt, joinStatement); - joinStatement.Rule = joinKindOpt + JOIN + tableAliasList + ON + joinConditions; - joinConditions.Rule = MakePlusRule(joinConditions, AND, joinCondition); - joinCondition.Rule = joinConditionArgument + "=" + joinConditionArgument; - joinConditionArgument.Rule = Id | boolean | string_literal | number | parameter; - joinKindOpt.Rule = Empty | "INNER" | "LEFT" | "RIGHT"; - - whereClauseOptional.Rule = Empty | "WHERE" + expression; - groupClauseOpt.Rule = Empty | "GROUP" + BY + idlist; - havingClauseOpt.Rule = Empty | "HAVING" + expression; - orderClauseOpt.Rule = Empty | "ORDER" + BY + orderList; - limitClauseOpt.Rule = Empty | "LIMIT" + expression; - offsetClauseOpt.Rule = Empty | "OFFSET" + expression; - - overPartitionByClauseOpt.Rule = Empty | "PARTITION" + BY + columnItemList; - overOrderByClauseOpt.Rule = Empty | "ORDER" + BY + orderList; - overArgumentsOpt.Rule = Empty | overPartitionByClauseOpt + overOrderByClauseOpt; - overClauseOpt.Rule = Empty | OVER + "(" + overArgumentsOpt + ")"; - - // Expression. - expressionList.Rule = MakePlusRule(expressionList, comma, expression); - expression.Rule = term | unExpr | binExpr | betweenExpr | inExpr | parameter; - term.Rule = Id | boolean | string_literal | number | funCall | tuple | parSelectStatement; - boolean.Rule = TRUE | FALSE; - tuple.Rule = "(" + expressionList + ")"; - parSelectStatement.Rule = "(" + selectStatement + ")"; - unExpr.Rule = unOp + term; - unOp.Rule = NOT | "+" | "-" | "~"; - binExpr.Rule = expression + binOp + expression; - binOp.Rule = ToTerm("+") | "-" | "*" | "/" | "%" // Arithmetic. - | "&" | "|" | "^" // Bit. - | "=" | ">" | "<" | ">=" | "<=" | "<>" | "!=" | "!<" | "!>" - | "AND" | "OR" | "LIKE" | "NOT LIKE"; - betweenExpr.Rule = expression + notOpt + "BETWEEN" + expression + "AND" + expression; - inExpr.Rule = expression + notOpt + "IN" + "(" + functionArguments + ")"; - notOpt.Rule = Empty | NOT; - - // 'funCall' covers some pseudo-operators and special forms like ANY(...), SOME(...), ALL(...), EXISTS(...), IN(...). - funCall.Rule = Id + "(" + functionArguments + ")"; - functionArguments.Rule = Empty | selectStatement | expressionList | "*"; - parameter.Rule = "@" + Id | "@" + Id + ":" + term; - - // Operators. - RegisterOperators(10, "*", "/", "%"); - RegisterOperators(9, "+", "-"); - RegisterOperators(8, "=", ">", "<", ">=", "<=", "<>", "!=", "!<", "!>", "LIKE", "IN"); - RegisterOperators(7, "^", "&", "|"); - RegisterOperators(6, NOT); - RegisterOperators(5, "AND"); - RegisterOperators(4, "OR"); - - MarkPunctuation(",", "(", ")"); - MarkPunctuation(asOpt, optionalSemicolon); - - // Note: we cannot declare binOp as transient because it includes operators "NOT LIKE", "NOT IN" consisting of two tokens. - // Transient non-terminals cannot have more than one non-punctuation child nodes. - // Instead, we set flag InheritPrecedence on binOp , so that it inherits precedence value from it's children, and this precedence is used - // in conflict resolution when binOp node is sitting on the stack. - MarkTransient(tableAliasItemOrSubQuery, term, asOpt, tableAliasOpt, columnAliasOpt, statementLine, expression, unOp, tuple); - binOp.SetFlag(TermFlags.InheritPrecedence); - } + var JOIN = ToTerm("JOIN"); + var BY = ToTerm("BY"); + var TRUE = ToTerm("TRUE"); + var FALSE = ToTerm("FALSE"); + var AND = ToTerm("AND"); + var OVER = ToTerm("OVER"); + var UNION = ToTerm("UNION"); + var ALL = ToTerm("ALL"); + var WITH = ToTerm("WITH"); + var CTE = TerminalFactory.CreateSqlExtIdentifier(this, "CTE"); + var ColumnAlias = TerminalFactory.CreateSqlExtIdentifier(this, "ColumnAlias"); + var TableAlias = TerminalFactory.CreateSqlExtIdentifier(this, "TableAlias"); + + // Non-terminals. + var Id = new NonTerminal("Id"); + var statement = new NonTerminal("stmt"); + var selectStatement = new NonTerminal("selectStatement"); + var idlist = new NonTerminal("idlist"); + var tableAliasList = new NonTerminal("tableAliasList"); + var tableAliasItem = new NonTerminal("tableAliasItem"); + var orderList = new NonTerminal("orderList"); + var orderMember = new NonTerminal("orderMember"); + var orderDirOptional = new NonTerminal("orderDirOpt"); + var whereClauseOptional = new NonTerminal("whereClauseOpt"); + var expression = new NonTerminal("expression"); + var expressionList = new NonTerminal("exprList"); + var optionalSelectRestriction = new NonTerminal("optionalSelectRestriction"); + var selectorList = new NonTerminal("selList"); + var fromClauseOpt = new NonTerminal("fromClauseOpt"); + var groupClauseOpt = new NonTerminal("groupClauseOpt"); + var havingClauseOpt = new NonTerminal("havingClauseOpt"); + var orderClauseOpt = new NonTerminal("orderClauseOpt"); + var limitClauseOpt = new NonTerminal("limitClauseOpt"); + var offsetClauseOpt = new NonTerminal("offsetClauseOpt"); + var columnItemList = new NonTerminal("columnItemList"); + var columnItem = new NonTerminal("columnItem"); + var columnSource = new NonTerminal("columnSource"); + var asOpt = new NonTerminal("asOpt"); + var tableAliasOpt = new NonTerminal("tableAliasOpt"); + var tuple = new NonTerminal("tuple"); + var joinChainOpt = new NonTerminal("joinChainOpt"); + var joinStatement = new NonTerminal("joinStatement"); + var joinKindOpt = new NonTerminal("joinKindOpt"); + var joinConditions = new NonTerminal("joinConditions"); + var joinCondition = new NonTerminal("joinCondition"); + var joinConditionArgument = new NonTerminal("joinConditionArgument"); + var term = new NonTerminal("term"); + var unExpr = new NonTerminal("unExpr"); + var unOp = new NonTerminal("unOp"); + var binExpr = new NonTerminal("binExpr"); + var binOp = new NonTerminal("binOp"); + var betweenExpr = new NonTerminal("betweenExpr"); + var inExpr = new NonTerminal("inExpr"); + var parSelectStatement = new NonTerminal("parSelectStmt"); + var notOpt = new NonTerminal("notOpt"); + var funCall = new NonTerminal("funCall"); + var parameter = new NonTerminal("parameter"); + var statementLine = new NonTerminal("stmtLine"); + var optionalSemicolon = new NonTerminal("semiOpt"); + var statementList = new NonTerminal("stmtList"); + var functionArguments = new NonTerminal("funArgs"); + var boolean = new NonTerminal("boolean"); + var overClauseOpt = new NonTerminal("overClauseOpt"); + var overArgumentsOpt = new NonTerminal("overArgumentsOpt"); + var overPartitionByClauseOpt = new NonTerminal("overPartitionByClauseOpt"); + var overOrderByClauseOpt = new NonTerminal("overOrderByClauseOpt"); + var unionStatementList = new NonTerminal("unionStmtList"); + var unionStatement = new NonTerminal("unionStmt"); + var unionClauseOpt = new NonTerminal("unionClauseOpt"); + var withClauseOpt = new NonTerminal("withClauseOpt"); + var cteList = new NonTerminal("cteList"); + var cte = new NonTerminal("cte"); + var cteColumnListOpt = new NonTerminal("cteColumnListOpt"); + var columnNames = new NonTerminal("columnNames"); + var IdColumn = new NonTerminal("IdColumn"); + var columnAliasOpt = new NonTerminal("columnAliasOpt"); + var IdTable = new NonTerminal("IdTable"); + var subQuery = new NonTerminal("subQuery"); + var tableAliasItemOrSubQuery = new NonTerminal("tableAliasItemOrSubQuery"); + var tableAliasOrSubQueryList = new NonTerminal("tableAliasOrSubQueryList"); + + // BNF Rules. + Root = statementList; + unionClauseOpt.Rule = Empty | UNION | UNION + ALL; + unionStatement.Rule = statement + unionClauseOpt; + unionStatementList.Rule = MakePlusRule(unionStatementList, unionStatement); + + statementLine.Rule = unionStatementList + optionalSemicolon; + optionalSemicolon.Rule = Empty | ";"; + statementList.Rule = MakePlusRule(statementList, statementLine); + + columnNames.Rule = MakePlusRule(columnNames, comma, Id_simple); + cteColumnListOpt.Rule = Empty | "(" + columnNames + ")"; + cte.Rule = CTE + cteColumnListOpt + AS + "(" + unionStatementList + ")"; + cteList.Rule = MakePlusRule(cteList, comma, cte); + withClauseOpt.Rule = Empty | WITH + cteList; + + statement.Rule = withClauseOpt + selectStatement; + + Id.Rule = MakePlusRule(Id, dot, Id_simple); + IdTable.Rule = MakePlusRule(IdTable, dot, TableAlias); + + tableAliasOpt.Rule = Empty | asOpt + IdTable; + IdColumn.Rule = MakePlusRule(IdColumn, dot, ColumnAlias); + columnAliasOpt.Rule = Empty | asOpt + IdColumn; + + asOpt.Rule = Empty | AS; + + idlist.Rule = MakePlusRule(idlist, comma, columnSource); + + tableAliasList.Rule = MakePlusRule(tableAliasList, comma, tableAliasItem); + tableAliasItem.Rule = Id + tableAliasOpt; + + subQuery.Rule = "(" + unionStatementList + ")" + AS + TableAlias; + tableAliasOrSubQueryList.Rule = MakePlusRule(tableAliasOrSubQueryList, comma, tableAliasItemOrSubQuery); + tableAliasItemOrSubQuery.Rule = tableAliasItem | subQuery; + + // Create Index. + orderList.Rule = MakePlusRule(orderList, comma, orderMember); + orderMember.Rule = Id + orderDirOptional; + orderDirOptional.Rule = Empty | "ASC" | "DESC"; + + // Select stmt. + selectStatement.Rule = SELECT + optionalSelectRestriction + selectorList + fromClauseOpt + whereClauseOptional + + groupClauseOpt + havingClauseOpt + orderClauseOpt + limitClauseOpt + offsetClauseOpt; + optionalSelectRestriction.Rule = Empty | "ALL" | "DISTINCT"; + selectorList.Rule = columnItemList | "*"; + columnItemList.Rule = MakePlusRule(columnItemList, comma, columnItem); + columnItem.Rule = columnSource + columnAliasOpt; + + columnSource.Rule = funCall + overClauseOpt | Id; + fromClauseOpt.Rule = Empty | FROM + tableAliasOrSubQueryList + joinChainOpt; + + joinChainOpt.Rule = MakeStarRule(joinChainOpt, joinStatement); + joinStatement.Rule = joinKindOpt + JOIN + tableAliasList + ON + joinConditions; + joinConditions.Rule = MakePlusRule(joinConditions, AND, joinCondition); + joinCondition.Rule = joinConditionArgument + "=" + joinConditionArgument; + joinConditionArgument.Rule = Id | boolean | string_literal | number | parameter; + joinKindOpt.Rule = Empty | "INNER" | "LEFT" | "RIGHT"; + + whereClauseOptional.Rule = Empty | "WHERE" + expression; + groupClauseOpt.Rule = Empty | "GROUP" + BY + idlist; + havingClauseOpt.Rule = Empty | "HAVING" + expression; + orderClauseOpt.Rule = Empty | "ORDER" + BY + orderList; + limitClauseOpt.Rule = Empty | "LIMIT" + expression; + offsetClauseOpt.Rule = Empty | "OFFSET" + expression; + + overPartitionByClauseOpt.Rule = Empty | "PARTITION" + BY + columnItemList; + overOrderByClauseOpt.Rule = Empty | "ORDER" + BY + orderList; + overArgumentsOpt.Rule = Empty | overPartitionByClauseOpt + overOrderByClauseOpt; + overClauseOpt.Rule = Empty | OVER + "(" + overArgumentsOpt + ")"; + + // Expression. + expressionList.Rule = MakePlusRule(expressionList, comma, expression); + expression.Rule = term | unExpr | binExpr | betweenExpr | inExpr | parameter; + term.Rule = Id | boolean | string_literal | number | funCall | tuple | parSelectStatement; + boolean.Rule = TRUE | FALSE; + tuple.Rule = "(" + expressionList + ")"; + parSelectStatement.Rule = "(" + selectStatement + ")"; + unExpr.Rule = unOp + term; + unOp.Rule = NOT | "+" | "-" | "~"; + binExpr.Rule = expression + binOp + expression; + binOp.Rule = ToTerm("+") | "-" | "*" | "/" | "%" // Arithmetic. + | "&" | "|" | "^" // Bit. + | "=" | ">" | "<" | ">=" | "<=" | "<>" | "!=" | "!<" | "!>" + | "AND" | "OR" | "LIKE" | "NOT LIKE"; + betweenExpr.Rule = expression + notOpt + "BETWEEN" + expression + "AND" + expression; + inExpr.Rule = expression + notOpt + "IN" + "(" + functionArguments + ")"; + notOpt.Rule = Empty | NOT; + + // 'funCall' covers some pseudo-operators and special forms like ANY(...), SOME(...), ALL(...), EXISTS(...), IN(...). + funCall.Rule = Id + "(" + functionArguments + ")"; + functionArguments.Rule = Empty | selectStatement | expressionList | "*"; + parameter.Rule = "@" + Id | "@" + Id + ":" + term; + + // Operators. + RegisterOperators(10, "*", "/", "%"); + RegisterOperators(9, "+", "-"); + RegisterOperators(8, "=", ">", "<", ">=", "<=", "<>", "!=", "!<", "!>", "LIKE", "IN"); + RegisterOperators(7, "^", "&", "|"); + RegisterOperators(6, NOT); + RegisterOperators(5, "AND"); + RegisterOperators(4, "OR"); + + MarkPunctuation(",", "(", ")"); + MarkPunctuation(asOpt, optionalSemicolon); + + // Note: we cannot declare binOp as transient because it includes operators "NOT LIKE", "NOT IN" consisting of two tokens. + // Transient non-terminals cannot have more than one non-punctuation child nodes. + // Instead, we set flag InheritPrecedence on binOp , so that it inherits precedence value from it's children, and this precedence is used + // in conflict resolution when binOp node is sitting on the stack. + MarkTransient(tableAliasItemOrSubQuery, term, asOpt, tableAliasOpt, columnAliasOpt, statementLine, expression, unOp, tuple); + binOp.SetFlag(TermFlags.InheritPrecedence); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs index 0445e0eee45..92323b2b927 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParser.cs @@ -5,728 +5,712 @@ using Irony.Parsing; using YesSql; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +public class SqlParser { - public class SqlParser + private readonly string _schema; + + private StringBuilder _builder; + private readonly IDictionary _parameters; + private readonly ISqlDialect _dialect; + private readonly string _tablePrefix; + private HashSet _tableAliases; + private HashSet _ctes; + private readonly ParseTree _tree; + private static readonly LanguageData _language = new(new SqlGrammar()); + private readonly Stack _modes; + + private string _limit; + private string _offset; + private string _select; + private string _from; + private string _where; + private string _having; + private string _groupBy; + private string _orderBy; + + private SqlParser( + ParseTree tree, + string schema, + ISqlDialect dialect, + string tablePrefix, + IDictionary parameters) { - private readonly string _schema; - - private StringBuilder _builder; - private readonly IDictionary _parameters; - private readonly ISqlDialect _dialect; - private readonly string _tablePrefix; - private HashSet _tableAliases; - private HashSet _ctes; - private readonly ParseTree _tree; - private static readonly LanguageData _language = new(new SqlGrammar()); - private readonly Stack _modes; - - private string _limit; - private string _offset; - private string _select; - private string _from; - private string _where; - private string _having; - private string _groupBy; - private string _orderBy; - - private SqlParser( - ParseTree tree, - string schema, - ISqlDialect dialect, - string tablePrefix, - IDictionary parameters) - { - _tree = tree; - _schema = schema; - _dialect = dialect; - _tablePrefix = tablePrefix; - _parameters = parameters; - _builder = new StringBuilder(tree.SourceText.Length); - _modes = new Stack(); - } - - public static bool TryParse(string sql, string schema, ISqlDialect dialect, string tablePrefix, IDictionary parameters, out string query, out IEnumerable messages) - { - try - { - var tree = new Parser(_language).Parse(sql); - - if (tree.HasErrors()) - { - query = null; - - messages = tree - .ParserMessages - .Select(x => $"{x.Message} at line:{x.Location.Line}, col:{x.Location.Column}") - .ToArray(); - - return false; - } - - var sqlParser = new SqlParser(tree, schema, dialect, tablePrefix, parameters); - query = sqlParser.Evaluate(); + _tree = tree; + _schema = schema; + _dialect = dialect; + _tablePrefix = tablePrefix; + _parameters = parameters; + _builder = new StringBuilder(tree.SourceText.Length); + _modes = new Stack(); + } - messages = Array.Empty(); + public static bool TryParse(string sql, string schema, ISqlDialect dialect, string tablePrefix, IDictionary parameters, out string query, out IEnumerable messages) + { + try + { + var tree = new Parser(_language).Parse(sql); - return true; - } - catch (SqlParserException se) + if (tree.HasErrors()) { query = null; - messages = new string[] { se.Message }; - } - catch (Exception e) - { - query = null; - messages = new string[] { "Unexpected error: " + e.Message }; - } - return false; - } + messages = tree + .ParserMessages + .Select(x => $"{x.Message} at line:{x.Location.Line}, col:{x.Location.Column}") + .ToArray(); - private string Evaluate() - { - PopulateAliases(_tree); - PopulateCteNames(_tree); - var statementList = _tree.Root; - - var statementsBuilder = new StringBuilder(); - - foreach (var unionStatementList in statementList.ChildNodes) - { - EvaluateStatementList(statementsBuilder, unionStatementList, true); + return false; } - statementsBuilder.Append(';'); + var sqlParser = new SqlParser(tree, schema, dialect, tablePrefix, parameters); + query = sqlParser.Evaluate(); - return statementsBuilder.ToString(); - } + messages = Array.Empty(); - private void PopulateAliases(ParseTree tree) + return true; + } + catch (SqlParserException se) { - // In order to determine if an Id is a table name or an alias, we - // analyze every Alias and store the value. - - _tableAliases = []; - - for (var i = 0; i < tree.Tokens.Count; i++) - { - if (tree.Tokens[i].Terminal.Name == "TableAlias") - { - _tableAliases.Add(tree.Tokens[i].ValueString); - } - } + query = null; + messages = new string[] { se.Message }; } - - private void PopulateCteNames(ParseTree tree) + catch (Exception e) { - _ctes = []; - - for (var i = 0; i < tree.Tokens.Count; i++) - { - if (tree.Tokens[i].Terminal.Name == "CTE") - { - _ctes.Add(tree.Tokens[i].ValueString); - } - } + query = null; + messages = new string[] { "Unexpected error: " + e.Message }; } - private string EvaluateSelectStatement(ParseTreeNode selectStatement) - { - ClearSelectStatement(); + return false; + } - var previousContent = _builder.Length > 0 ? _builder.ToString() : null; - _builder.Clear(); + private string Evaluate() + { + PopulateAliases(_tree); + PopulateCteNames(_tree); + var statementList = _tree.Root; - var sqlBuilder = _dialect.CreateBuilder(_tablePrefix); + var statementsBuilder = new StringBuilder(); - EvaluateSelectRestriction(selectStatement.ChildNodes[1]); - EvaluateSelectorList(selectStatement.ChildNodes[2]); + foreach (var unionStatementList in statementList.ChildNodes) + { + EvaluateStatementList(statementsBuilder, unionStatementList, true); + } - sqlBuilder.Select(); - sqlBuilder.Selector(_select); + statementsBuilder.Append(';'); - EvaluateFromClause(selectStatement.ChildNodes[3]); + return statementsBuilder.ToString(); + } - if (!string.IsNullOrEmpty(_from)) - { - sqlBuilder.From(_from); - } + private void PopulateAliases(ParseTree tree) + { + // In order to determine if an Id is a table name or an alias, we + // analyze every Alias and store the value. - EvaluateWhereClause(selectStatement.ChildNodes[4]); + _tableAliases = []; - if (!string.IsNullOrEmpty(_where)) + for (var i = 0; i < tree.Tokens.Count; i++) + { + if (tree.Tokens[i].Terminal.Name == "TableAlias") { - sqlBuilder.WhereAnd(_where); + _tableAliases.Add(tree.Tokens[i].ValueString); } + } + } - EvaluateGroupClause(selectStatement.ChildNodes[5]); + private void PopulateCteNames(ParseTree tree) + { + _ctes = []; - if (!string.IsNullOrEmpty(_groupBy)) + for (var i = 0; i < tree.Tokens.Count; i++) + { + if (tree.Tokens[i].Terminal.Name == "CTE") { - sqlBuilder.GroupBy(_groupBy); + _ctes.Add(tree.Tokens[i].ValueString); } + } + } - EvaluateHavingClause(selectStatement.ChildNodes[6]); + private string EvaluateSelectStatement(ParseTreeNode selectStatement) + { + ClearSelectStatement(); - if (!string.IsNullOrEmpty(_having)) - { - sqlBuilder.Having(_having); - } + var previousContent = _builder.Length > 0 ? _builder.ToString() : null; + _builder.Clear(); - EvaluateOrderClause(selectStatement.ChildNodes[7]); + var sqlBuilder = _dialect.CreateBuilder(_tablePrefix); - if (!string.IsNullOrEmpty(_orderBy)) - { - sqlBuilder.OrderBy(_orderBy); - } + EvaluateSelectRestriction(selectStatement.ChildNodes[1]); + EvaluateSelectorList(selectStatement.ChildNodes[2]); - EvaluateLimitClause(selectStatement.ChildNodes[8]); + sqlBuilder.Select(); + sqlBuilder.Selector(_select); - if (!string.IsNullOrEmpty(_limit)) - { - sqlBuilder.Take(_limit); - } + EvaluateFromClause(selectStatement.ChildNodes[3]); - EvaluateOffsetClause(selectStatement.ChildNodes[9]); + if (!string.IsNullOrEmpty(_from)) + { + sqlBuilder.From(_from); + } - if (!string.IsNullOrEmpty(_offset)) - { - sqlBuilder.Skip(_offset); - } + EvaluateWhereClause(selectStatement.ChildNodes[4]); - if (previousContent != null) - { - _builder.Clear(); - _builder.Append(new StringBuilder(previousContent)); - } + if (!string.IsNullOrEmpty(_where)) + { + sqlBuilder.WhereAnd(_where); + } - ClearSelectStatement(); + EvaluateGroupClause(selectStatement.ChildNodes[5]); - return sqlBuilder.ToSqlString(); + if (!string.IsNullOrEmpty(_groupBy)) + { + sqlBuilder.GroupBy(_groupBy); } - private void EvaluateLimitClause(ParseTreeNode parseTreeNode) + EvaluateHavingClause(selectStatement.ChildNodes[6]); + + if (!string.IsNullOrEmpty(_having)) { - if (parseTreeNode.ChildNodes.Count == 0) - { - return; - } + sqlBuilder.Having(_having); + } - _builder.Clear(); + EvaluateOrderClause(selectStatement.ChildNodes[7]); - // Evaluating so that the value can be transformed as a parameter. - EvaluateExpression(parseTreeNode.ChildNodes[1]); + if (!string.IsNullOrEmpty(_orderBy)) + { + sqlBuilder.OrderBy(_orderBy); + } + + EvaluateLimitClause(selectStatement.ChildNodes[8]); - _limit = _builder.ToString(); + if (!string.IsNullOrEmpty(_limit)) + { + sqlBuilder.Take(_limit); } - private void EvaluateOffsetClause(ParseTreeNode parseTreeNode) + EvaluateOffsetClause(selectStatement.ChildNodes[9]); + + if (!string.IsNullOrEmpty(_offset)) { - if (parseTreeNode.ChildNodes.Count == 0) - { - return; - } + sqlBuilder.Skip(_offset); + } + if (previousContent != null) + { _builder.Clear(); + _builder.Append(new StringBuilder(previousContent)); + } - // Evaluating so that the value can be transformed as a parameter. - EvaluateExpression(parseTreeNode.ChildNodes[1]); + ClearSelectStatement(); - _offset = _builder.ToString(); - } + return sqlBuilder.ToSqlString(); + } - private void EvaluateOrderClause(ParseTreeNode parseTreeNode) + private void EvaluateLimitClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) { - if (parseTreeNode.ChildNodes.Count == 0) - { - return; - } + return; + } - _builder.Clear(); + _builder.Clear(); - var idList = parseTreeNode.ChildNodes[2]; + // Evaluating so that the value can be transformed as a parameter. + EvaluateExpression(parseTreeNode.ChildNodes[1]); - _modes.Push(FormattingModes.SelectClause); - for (var i = 0; i < idList.ChildNodes.Count; i++) - { - var id = idList.ChildNodes[i].ChildNodes[0]; + _limit = _builder.ToString(); + } - if (i > 0) - { - _builder.Append(", "); - } + private void EvaluateOffsetClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) + { + return; + } - EvaluateId(id); + _builder.Clear(); - if (idList.ChildNodes[i].ChildNodes[1].ChildNodes.Count > 0) - { - _builder.Append(' ').Append(idList.ChildNodes[i].ChildNodes[1].ChildNodes[0].Term.Name); - } - } + // Evaluating so that the value can be transformed as a parameter. + EvaluateExpression(parseTreeNode.ChildNodes[1]); - _orderBy = _builder.ToString(); + _offset = _builder.ToString(); + } - _modes.Pop(); + private void EvaluateOrderClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) + { + return; } - private void EvaluateHavingClause(ParseTreeNode parseTreeNode) + _builder.Clear(); + + var idList = parseTreeNode.ChildNodes[2]; + + _modes.Push(FormattingModes.SelectClause); + for (var i = 0; i < idList.ChildNodes.Count; i++) { - if (parseTreeNode.ChildNodes.Count == 0) + var id = idList.ChildNodes[i].ChildNodes[0]; + + if (i > 0) { - return; + _builder.Append(", "); } - _builder.Clear(); + EvaluateId(id); - _modes.Push(FormattingModes.SelectClause); - EvaluateExpression(parseTreeNode.ChildNodes[1]); + if (idList.ChildNodes[i].ChildNodes[1].ChildNodes.Count > 0) + { + _builder.Append(' ').Append(idList.ChildNodes[i].ChildNodes[1].ChildNodes[0].Term.Name); + } + } - _having = _builder.ToString(); + _orderBy = _builder.ToString(); - _modes.Pop(); - } + _modes.Pop(); + } - private void EvaluateGroupClause(ParseTreeNode parseTreeNode) + private void EvaluateHavingClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) { - if (parseTreeNode.ChildNodes.Count == 0) - { - return; - } + return; + } - _builder.Clear(); + _builder.Clear(); - var idList = parseTreeNode.ChildNodes[2]; + _modes.Push(FormattingModes.SelectClause); + EvaluateExpression(parseTreeNode.ChildNodes[1]); - _modes.Push(FormattingModes.SelectClause); - for (var i = 0; i < idList.ChildNodes.Count; i++) - { - var columnSource = idList.ChildNodes[i]; + _having = _builder.ToString(); - if (i > 0) - { - _builder.Append(", "); - } + _modes.Pop(); + } - if (columnSource.ChildNodes[0].Term.Name == "Id") - { - EvaluateId(columnSource.ChildNodes[0]); - } - else - { - EvaluateFunCall(columnSource.ChildNodes[0]); - } - } + private void EvaluateGroupClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) + { + return; + } - _groupBy = _builder.ToString(); + _builder.Clear(); - _modes.Pop(); - } + var idList = parseTreeNode.ChildNodes[2]; - private void EvaluateWhereClause(ParseTreeNode parseTreeNode) + _modes.Push(FormattingModes.SelectClause); + for (var i = 0; i < idList.ChildNodes.Count; i++) { - if (parseTreeNode.ChildNodes.Count == 0) + var columnSource = idList.ChildNodes[i]; + + if (i > 0) { - // EMPTY - return; + _builder.Append(", "); } - _builder.Clear(); + if (columnSource.ChildNodes[0].Term.Name == "Id") + { + EvaluateId(columnSource.ChildNodes[0]); + } + else + { + EvaluateFunCall(columnSource.ChildNodes[0]); + } + } - _modes.Push(FormattingModes.SelectClause); - EvaluateExpression(parseTreeNode.ChildNodes[1]); + _groupBy = _builder.ToString(); - _where = _builder.ToString(); + _modes.Pop(); + } - _modes.Pop(); + private void EvaluateWhereClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) + { + // EMPTY + return; } - private void EvaluateExpression(ParseTreeNode parseTreeNode) + _builder.Clear(); + + _modes.Push(FormattingModes.SelectClause); + EvaluateExpression(parseTreeNode.ChildNodes[1]); + + _where = _builder.ToString(); + + _modes.Pop(); + } + + private void EvaluateExpression(ParseTreeNode parseTreeNode) + { + switch (parseTreeNode.Term.Name) { - switch (parseTreeNode.Term.Name) - { - case "unExpr": - _builder.Append(parseTreeNode.ChildNodes[0].Term.Name); - EvaluateExpression(parseTreeNode.ChildNodes[1]); - break; - case "binExpr": - EvaluateExpression(parseTreeNode.ChildNodes[0]); - _builder.Append(' '); - _builder.Append(parseTreeNode.ChildNodes[1].ChildNodes[0].Term.Name).Append(' '); - EvaluateExpression(parseTreeNode.ChildNodes[2]); - break; - case "betweenExpr": - EvaluateExpression(parseTreeNode.ChildNodes[0]); - _builder.Append(' '); - if (parseTreeNode.ChildNodes[1].ChildNodes.Count > 0) - { - _builder.Append("NOT "); - } - _builder.Append("BETWEEN "); - EvaluateExpression(parseTreeNode.ChildNodes[3]); - _builder.Append(' '); - _builder.Append("AND "); - EvaluateExpression(parseTreeNode.ChildNodes[5]); - break; - case "inExpr": - EvaluateExpression(parseTreeNode.ChildNodes[0]); - _builder.Append(' '); - if (parseTreeNode.ChildNodes[1].ChildNodes.Count > 0) + case "unExpr": + _builder.Append(parseTreeNode.ChildNodes[0].Term.Name); + EvaluateExpression(parseTreeNode.ChildNodes[1]); + break; + case "binExpr": + EvaluateExpression(parseTreeNode.ChildNodes[0]); + _builder.Append(' '); + _builder.Append(parseTreeNode.ChildNodes[1].ChildNodes[0].Term.Name).Append(' '); + EvaluateExpression(parseTreeNode.ChildNodes[2]); + break; + case "betweenExpr": + EvaluateExpression(parseTreeNode.ChildNodes[0]); + _builder.Append(' '); + if (parseTreeNode.ChildNodes[1].ChildNodes.Count > 0) + { + _builder.Append("NOT "); + } + _builder.Append("BETWEEN "); + EvaluateExpression(parseTreeNode.ChildNodes[3]); + _builder.Append(' '); + _builder.Append("AND "); + EvaluateExpression(parseTreeNode.ChildNodes[5]); + break; + case "inExpr": + EvaluateExpression(parseTreeNode.ChildNodes[0]); + _builder.Append(' '); + if (parseTreeNode.ChildNodes[1].ChildNodes.Count > 0) + { + _builder.Append("NOT "); + } + _builder.Append("IN ("); + EvaluateInArgs(parseTreeNode.ChildNodes[3]); + _builder.Append(')'); + break; + // Term and Tuple are transient, so they appear directly. + case "Id": + EvaluateId(parseTreeNode); + break; + case "boolean": + _builder.Append(_dialect.GetSqlValue(parseTreeNode.ChildNodes[0].Term.Name == "TRUE")); + break; + case "string": + _builder.Append(_dialect.GetSqlValue(parseTreeNode.Token.ValueString)); + break; + case "number": + _builder.Append(_dialect.GetSqlValue(parseTreeNode.Token.Value)); + break; + case "funCall": + EvaluateFunCall(parseTreeNode); + break; + case "exprList": + _builder.Append('('); + EvaluateExpression(parseTreeNode.ChildNodes[0]); + _builder.Append(')'); + break; + case "parSelectStmt": + _builder.Append('('); + _builder.Append(EvaluateSelectStatement(parseTreeNode.ChildNodes[0])); + _builder.Append(')'); + break; + case "parameter": + var name = parseTreeNode.ChildNodes[1].ChildNodes[0].Token.ValueString; + + _builder.Append("@" + name); + + if (_parameters != null && !_parameters.ContainsKey(name)) + { + // If a parameter is not set and there is no default value, report it. + if (parseTreeNode.ChildNodes.Count < 3) { - _builder.Append("NOT "); + throw new SqlParserException("Missing parameters: " + name); } - _builder.Append("IN ("); - EvaluateInArgs(parseTreeNode.ChildNodes[3]); - _builder.Append(')'); - break; - // Term and Tuple are transient, so they appear directly. - case "Id": - EvaluateId(parseTreeNode); - break; - case "boolean": - _builder.Append(_dialect.GetSqlValue(parseTreeNode.ChildNodes[0].Term.Name == "TRUE")); - break; - case "string": - _builder.Append(_dialect.GetSqlValue(parseTreeNode.Token.ValueString)); - break; - case "number": - _builder.Append(_dialect.GetSqlValue(parseTreeNode.Token.Value)); - break; - case "funCall": - EvaluateFunCall(parseTreeNode); - break; - case "exprList": - _builder.Append('('); - EvaluateExpression(parseTreeNode.ChildNodes[0]); - _builder.Append(')'); - break; - case "parSelectStmt": - _builder.Append('('); - _builder.Append(EvaluateSelectStatement(parseTreeNode.ChildNodes[0])); - _builder.Append(')'); - break; - case "parameter": - var name = parseTreeNode.ChildNodes[1].ChildNodes[0].Token.ValueString; - - _builder.Append("@" + name); - - if (_parameters != null && !_parameters.ContainsKey(name)) + else { - // If a parameter is not set and there is no default value, report it. - if (parseTreeNode.ChildNodes.Count < 3) + if (parseTreeNode.ChildNodes[3].Token != null) { - throw new SqlParserException("Missing parameters: " + name); + _parameters[name] = parseTreeNode.ChildNodes[3].Token.Value; } else { - if (parseTreeNode.ChildNodes[3].Token != null) + // Example: true. + if (parseTreeNode.ChildNodes[3].ChildNodes[0].Token != null) { - _parameters[name] = parseTreeNode.ChildNodes[3].Token.Value; + _parameters[name] = parseTreeNode.ChildNodes[3].ChildNodes[0].Token.Value; } else { - // Example: true. - if (parseTreeNode.ChildNodes[3].ChildNodes[0].Token != null) - { - _parameters[name] = parseTreeNode.ChildNodes[3].ChildNodes[0].Token.Value; - } - else - { - throw new SqlParserException("Unsupported syntax for parameter: " + name); - } + throw new SqlParserException("Unsupported syntax for parameter: " + name); } } } + } - break; - case "*": - _builder.Append('*'); - break; - } + break; + case "*": + _builder.Append('*'); + break; } + } - private void EvaluateInArgs(ParseTreeNode inArgs) + private void EvaluateInArgs(ParseTreeNode inArgs) + { + if (inArgs.ChildNodes[0].Term.Name == "selectStatement") { - if (inArgs.ChildNodes[0].Term.Name == "selectStatement") - { - // 'selectStatement'. - _builder.Append(EvaluateSelectStatement(inArgs.ChildNodes[0])); - } - else - { - // 'expressionList'. - EvaluateExpressionList(inArgs.ChildNodes[0]); - } + // 'selectStatement'. + _builder.Append(EvaluateSelectStatement(inArgs.ChildNodes[0])); } - - private void EvaluateFunCall(ParseTreeNode funCall) + else { - var funcName = funCall.ChildNodes[0].ChildNodes[0].Token.ValueString; - IList arguments; - var tempBuilder = _builder; + // 'expressionList'. + EvaluateExpressionList(inArgs.ChildNodes[0]); + } + } - if (funCall.ChildNodes[1].ChildNodes.Count == 0) - { - arguments = Array.Empty(); - } - else if (funCall.ChildNodes[1].ChildNodes[0].Term.Name == "selectStatement") + private void EvaluateFunCall(ParseTreeNode funCall) + { + var funcName = funCall.ChildNodes[0].ChildNodes[0].Token.ValueString; + IList arguments; + var tempBuilder = _builder; + + if (funCall.ChildNodes[1].ChildNodes.Count == 0) + { + arguments = Array.Empty(); + } + else if (funCall.ChildNodes[1].ChildNodes[0].Term.Name == "selectStatement") + { + // 'selectStatement'. + _builder = new StringBuilder(); + _builder.Append(EvaluateSelectStatement(funCall.ChildNodes[1].ChildNodes[0])); + arguments = new string[] { _builder.ToString() }; + _builder = tempBuilder; + } + else if (funCall.ChildNodes[1].ChildNodes[0].Term.Name == "*") + { + arguments = new string[] { "*" }; + } + else + { + // 'expressionList'. + arguments = new List(); + for (var i = 0; i < funCall.ChildNodes[1].ChildNodes[0].ChildNodes.Count; i++) { - // 'selectStatement'. _builder = new StringBuilder(); - _builder.Append(EvaluateSelectStatement(funCall.ChildNodes[1].ChildNodes[0])); - arguments = new string[] { _builder.ToString() }; + EvaluateExpression(funCall.ChildNodes[1].ChildNodes[0].ChildNodes[i]); + arguments.Add(_builder.ToString()); _builder = tempBuilder; } - else if (funCall.ChildNodes[1].ChildNodes[0].Term.Name == "*") - { - arguments = new string[] { "*" }; - } - else - { - // 'expressionList'. - arguments = new List(); - for (var i = 0; i < funCall.ChildNodes[1].ChildNodes[0].ChildNodes.Count; i++) - { - _builder = new StringBuilder(); - EvaluateExpression(funCall.ChildNodes[1].ChildNodes[0].ChildNodes[i]); - arguments.Add(_builder.ToString()); - _builder = tempBuilder; - } - } - - _builder.Append(_dialect.RenderMethod(funcName, arguments.ToArray())); } - private void EvaluateExpressionList(ParseTreeNode expressionList) + _builder.Append(_dialect.RenderMethod(funcName, arguments.ToArray())); + } + + private void EvaluateExpressionList(ParseTreeNode expressionList) + { + for (var i = 0; i < expressionList.ChildNodes.Count; i++) { - for (var i = 0; i < expressionList.ChildNodes.Count; i++) + if (i > 0) { - if (i > 0) - { - _builder.Append(", "); - } - - EvaluateExpression(expressionList.ChildNodes[i]); + _builder.Append(", "); } + + EvaluateExpression(expressionList.ChildNodes[i]); } + } - private void EvaluateFromClause(ParseTreeNode parseTreeNode) + private void EvaluateFromClause(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count == 0) { - if (parseTreeNode.ChildNodes.Count == 0) - { - // 'EMPTY'. - return; - } + // 'EMPTY'. + return; + } - _builder.Clear(); + _builder.Clear(); - var aliasList = parseTreeNode.ChildNodes[1]; + var aliasList = parseTreeNode.ChildNodes[1]; - _modes.Push(FormattingModes.FromClause); + _modes.Push(FormattingModes.FromClause); - EvaluateAliasOrSubQueryList(aliasList); + EvaluateAliasOrSubQueryList(aliasList); - _modes.Pop(); + _modes.Pop(); - var joins = parseTreeNode.ChildNodes[2]; + var joins = parseTreeNode.ChildNodes[2]; - // Process join statements. - if (joins.ChildNodes.Count != 0) + // Process join statements. + if (joins.ChildNodes.Count != 0) + { + foreach (var joinStatement in joins.ChildNodes) { - foreach (var joinStatement in joins.ChildNodes) - { - _modes.Push(FormattingModes.FromClause); + _modes.Push(FormattingModes.FromClause); - var jointKindOpt = joinStatement.ChildNodes[0]; + var jointKindOpt = joinStatement.ChildNodes[0]; - if (jointKindOpt.ChildNodes.Count > 0) - { - _builder.Append(' ').Append(jointKindOpt.ChildNodes[0].Term.Name); - } + if (jointKindOpt.ChildNodes.Count > 0) + { + _builder.Append(' ').Append(jointKindOpt.ChildNodes[0].Term.Name); + } - _builder.Append(" JOIN "); + _builder.Append(" JOIN "); - EvaluateAliasList(joinStatement.ChildNodes[2]); + EvaluateAliasList(joinStatement.ChildNodes[2]); - _builder.Append(" ON "); + _builder.Append(" ON "); - var joinConditions = joinStatement.ChildNodes[4].ChildNodes; + var joinConditions = joinStatement.ChildNodes[4].ChildNodes; - for (var i = 0; i < joinConditions.Count; i++) + for (var i = 0; i < joinConditions.Count; i++) + { + if (i > 0) { - if (i > 0) - { - _builder.Append(" AND "); - } - _modes.Push(FormattingModes.SelectClause); - var joinCondition = joinConditions[i]; - EvaluateExpression(joinCondition.ChildNodes[0].ChildNodes[0]); - _builder.Append(" = "); - EvaluateExpression(joinCondition.ChildNodes[2].ChildNodes[0]); - _modes.Pop(); + _builder.Append(" AND "); } + _modes.Push(FormattingModes.SelectClause); + var joinCondition = joinConditions[i]; + EvaluateExpression(joinCondition.ChildNodes[0].ChildNodes[0]); + _builder.Append(" = "); + EvaluateExpression(joinCondition.ChildNodes[2].ChildNodes[0]); + _modes.Pop(); } } - - _from = _builder.ToString(); } - private void EvaluateAliasList(ParseTreeNode aliasList) + _from = _builder.ToString(); + } + + private void EvaluateAliasList(ParseTreeNode aliasList) + { + for (var i = 0; i < aliasList.ChildNodes.Count; i++) { - for (var i = 0; i < aliasList.ChildNodes.Count; i++) - { - var aliasItem = aliasList.ChildNodes[i]; + var aliasItem = aliasList.ChildNodes[i]; - if (i > 0) - { - _builder.Append(", "); - } + if (i > 0) + { + _builder.Append(", "); + } - EvaluateId(aliasItem.ChildNodes[0]); + EvaluateId(aliasItem.ChildNodes[0]); - if (aliasItem.ChildNodes.Count > 1) - { - EvaluateAliasOptional(aliasItem.ChildNodes[1]); - } + if (aliasItem.ChildNodes.Count > 1) + { + EvaluateAliasOptional(aliasItem.ChildNodes[1]); } } + } - private void EvaluateAliasOrSubQueryList(ParseTreeNode aliasList) + private void EvaluateAliasOrSubQueryList(ParseTreeNode aliasList) + { + for (var i = 0; i < aliasList.ChildNodes.Count; i++) { - for (var i = 0; i < aliasList.ChildNodes.Count; i++) + var aliasItemOrSubQuery = aliasList.ChildNodes[i]; + + if (i > 0) { - var aliasItemOrSubQuery = aliasList.ChildNodes[i]; + _builder.Append(", "); + } - if (i > 0) - { - _builder.Append(", "); - } + if (aliasItemOrSubQuery.Term.Name == "tableAliasItem") + { + EvaluateId(aliasItemOrSubQuery.ChildNodes[0]); - if (aliasItemOrSubQuery.Term.Name == "tableAliasItem") + if (aliasItemOrSubQuery.ChildNodes.Count > 1) { - EvaluateId(aliasItemOrSubQuery.ChildNodes[0]); - - if (aliasItemOrSubQuery.ChildNodes.Count > 1) - { - EvaluateAliasOptional(aliasItemOrSubQuery.ChildNodes[1]); - } + EvaluateAliasOptional(aliasItemOrSubQuery.ChildNodes[1]); } - else if (aliasItemOrSubQuery.Term.Name == "subQuery") - { - _builder.Append('('); + } + else if (aliasItemOrSubQuery.Term.Name == "subQuery") + { + _builder.Append('('); - EvaluateStatementList(_builder, aliasItemOrSubQuery.ChildNodes[0], false); + EvaluateStatementList(_builder, aliasItemOrSubQuery.ChildNodes[0], false); - _builder.Append(") AS "); - _builder.Append(aliasItemOrSubQuery.ChildNodes[2].Token.ValueString); - } + _builder.Append(") AS "); + _builder.Append(aliasItemOrSubQuery.ChildNodes[2].Token.ValueString); } } + } + + private void EvaluateSelectorList(ParseTreeNode parseTreeNode) + { + var selectorList = parseTreeNode.ChildNodes[0]; - private void EvaluateSelectorList(ParseTreeNode parseTreeNode) + if (selectorList.Term.Name == "*") { - var selectorList = parseTreeNode.ChildNodes[0]; + _builder.Append('*'); + } + else + { + _modes.Push(FormattingModes.SelectClause); - if (selectorList.Term.Name == "*") + // 'columnItemList'. + for (var i = 0; i < selectorList.ChildNodes.Count; i++) { - _builder.Append('*'); - } - else - { - _modes.Push(FormattingModes.SelectClause); - - // 'columnItemList'. - for (var i = 0; i < selectorList.ChildNodes.Count; i++) + if (i > 0) { - if (i > 0) - { - _builder.Append(", "); - } - - var columnItem = selectorList.ChildNodes[i]; + _builder.Append(", "); + } - // 'columnItem'. - var columnSource = columnItem.ChildNodes[0]; - var funCallOrId = columnSource.ChildNodes[0]; - if (funCallOrId.Term.Name == "Id") - { - EvaluateId(funCallOrId); - } - else - { - EvaluateFunCall(funCallOrId); - var overClauseOpt = columnSource.ChildNodes[1]; - if (overClauseOpt.ChildNodes.Count > 0) - { - EvaluateOverClauseOptional(overClauseOpt); - } - } + var columnItem = selectorList.ChildNodes[i]; - if (columnItem.ChildNodes.Count > 1) + // 'columnItem'. + var columnSource = columnItem.ChildNodes[0]; + var funCallOrId = columnSource.ChildNodes[0]; + if (funCallOrId.Term.Name == "Id") + { + EvaluateId(funCallOrId); + } + else + { + EvaluateFunCall(funCallOrId); + var overClauseOpt = columnSource.ChildNodes[1]; + if (overClauseOpt.ChildNodes.Count > 0) { - // 'AS'. - EvaluateAliasOptional(columnItem.ChildNodes[1]); + EvaluateOverClauseOptional(overClauseOpt); } } - _modes.Pop(); + if (columnItem.ChildNodes.Count > 1) + { + // 'AS'. + EvaluateAliasOptional(columnItem.ChildNodes[1]); + } } - _select = _builder.ToString(); + _modes.Pop(); } - private void EvaluateId(ParseTreeNode id) + _select = _builder.ToString(); + } + + private void EvaluateId(ParseTreeNode id) + { + switch (_modes.Peek()) { - switch (_modes.Peek()) - { - case FormattingModes.SelectClause: - EvaluateSelectId(id); - break; - case FormattingModes.FromClause: - EvaluateFromId(id); - break; - } + case FormattingModes.SelectClause: + EvaluateSelectId(id); + break; + case FormattingModes.FromClause: + EvaluateFromId(id); + break; } + } - private void EvaluateSelectId(ParseTreeNode id) + private void EvaluateSelectId(ParseTreeNode id) + { + for (var i = 0; i < id.ChildNodes.Count; i++) { - for (var i = 0; i < id.ChildNodes.Count; i++) + if (i == 0 && id.ChildNodes.Count > 1 && !_tableAliases.Contains(id.ChildNodes[i].Token.ValueString)) { - if (i == 0 && id.ChildNodes.Count > 1 && !_tableAliases.Contains(id.ChildNodes[i].Token.ValueString)) - { - _builder.Append(_dialect.QuoteForTableName(_tablePrefix + id.ChildNodes[i].Token.ValueString, _schema)); - } - else if (i == 0 && id.ChildNodes.Count == 1) - { - _builder.Append(_dialect.QuoteForColumnName(id.ChildNodes[i].Token.ValueString)); - } - else + _builder.Append(_dialect.QuoteForTableName(_tablePrefix + id.ChildNodes[i].Token.ValueString, _schema)); + } + else if (i == 0 && id.ChildNodes.Count == 1) + { + _builder.Append(_dialect.QuoteForColumnName(id.ChildNodes[i].Token.ValueString)); + } + else + { + if (i > 0) { - if (i > 0) - { - _builder.Append('.'); - } - - if (_tableAliases.Contains(id.ChildNodes[i].Token.ValueString)) - { - _builder.Append(id.ChildNodes[i].Token.ValueString); - } - else - { - _builder.Append(_dialect.QuoteForColumnName(id.ChildNodes[i].Token.ValueString)); - } + _builder.Append('.'); } - } - } - private void EvaluateFromId(ParseTreeNode id) - { - for (var i = 0; i < id.ChildNodes.Count; i++) - { - if (i == 0 && !_tableAliases.Contains(id.ChildNodes[i].Token.ValueString) && !_ctes.Contains(id.ChildNodes[i].Token.ValueString)) + if (_tableAliases.Contains(id.ChildNodes[i].Token.ValueString)) { - _builder.Append(_dialect.QuoteForTableName(_tablePrefix + id.ChildNodes[i].Token.ValueString, _schema)); + _builder.Append(id.ChildNodes[i].Token.ValueString); } else { @@ -734,183 +718,198 @@ private void EvaluateFromId(ParseTreeNode id) } } } + } - private void EvaluateAliasOptional(ParseTreeNode parseTreeNode) + private void EvaluateFromId(ParseTreeNode id) + { + for (var i = 0; i < id.ChildNodes.Count; i++) { - if (parseTreeNode.ChildNodes.Count > 0) + if (i == 0 && !_tableAliases.Contains(id.ChildNodes[i].Token.ValueString) && !_ctes.Contains(id.ChildNodes[i].Token.ValueString)) { - _builder.Append(" AS "); - _builder.Append(parseTreeNode.ChildNodes[0].Token.ValueString); + _builder.Append(_dialect.QuoteForTableName(_tablePrefix + id.ChildNodes[i].Token.ValueString, _schema)); + } + else + { + _builder.Append(_dialect.QuoteForColumnName(id.ChildNodes[i].Token.ValueString)); } } + } - private void EvaluateSelectRestriction(ParseTreeNode parseTreeNode) + private void EvaluateAliasOptional(ParseTreeNode parseTreeNode) + { + if (parseTreeNode.ChildNodes.Count > 0) { - _builder.Clear(); - - if (parseTreeNode.ChildNodes.Count > 0) - { - _builder.Append(parseTreeNode.ChildNodes[0].Term.Name).Append(' '); - } + _builder.Append(" AS "); + _builder.Append(parseTreeNode.ChildNodes[0].Token.ValueString); } + } - private void EvaluateOverClauseOptional(ParseTreeNode overClauseOpt) + private void EvaluateSelectRestriction(ParseTreeNode parseTreeNode) + { + _builder.Clear(); + + if (parseTreeNode.ChildNodes.Count > 0) { - var overArgumentsOpt = overClauseOpt.ChildNodes[1]; + _builder.Append(parseTreeNode.ChildNodes[0].Term.Name).Append(' '); + } + } - _builder.Append(" OVER "); - _builder.Append('('); + private void EvaluateOverClauseOptional(ParseTreeNode overClauseOpt) + { + var overArgumentsOpt = overClauseOpt.ChildNodes[1]; - if (overArgumentsOpt.ChildNodes.Count == 0) - { - _builder.Append(')'); - return; - } + _builder.Append(" OVER "); + _builder.Append('('); + + if (overArgumentsOpt.ChildNodes.Count == 0) + { + _builder.Append(')'); + return; + } - var overPartitionByClauseOpt = overArgumentsOpt.ChildNodes[0]; - var overOrderByClauseOpt = overArgumentsOpt.ChildNodes[1]; + var overPartitionByClauseOpt = overArgumentsOpt.ChildNodes[0]; + var overOrderByClauseOpt = overArgumentsOpt.ChildNodes[1]; - var hasOverPartitionByClause = overPartitionByClauseOpt.ChildNodes.Count > 0; - var hasOverOrderByClause = overOrderByClauseOpt.ChildNodes.Count > 0; + var hasOverPartitionByClause = overPartitionByClauseOpt.ChildNodes.Count > 0; + var hasOverOrderByClause = overOrderByClauseOpt.ChildNodes.Count > 0; - if (hasOverPartitionByClause) + if (hasOverPartitionByClause) + { + _builder.Append("PARTITION BY "); + var columnItemList = overPartitionByClauseOpt.ChildNodes[2]; + for (var i = 0; i < columnItemList.ChildNodes.Count; i++) { - _builder.Append("PARTITION BY "); - var columnItemList = overPartitionByClauseOpt.ChildNodes[2]; - for (var i = 0; i < columnItemList.ChildNodes.Count; i++) + if (i > 0) { - if (i > 0) - { - _builder.Append(", "); - } - var columnItem = columnItemList.ChildNodes[i]; - var id = columnItem.ChildNodes[0].ChildNodes[0]; - EvaluateSelectId(id); + _builder.Append(", "); } + var columnItem = columnItemList.ChildNodes[i]; + var id = columnItem.ChildNodes[0].ChildNodes[0]; + EvaluateSelectId(id); } + } - if (hasOverOrderByClause) + if (hasOverOrderByClause) + { + if (hasOverPartitionByClause) { - if (hasOverPartitionByClause) - { - _builder.Append(' '); - } + _builder.Append(' '); + } - _builder.Append("ORDER BY "); + _builder.Append("ORDER BY "); - var orderList = overOrderByClauseOpt.ChildNodes[2]; - for (var i = 0; i < orderList.ChildNodes.Count; i++) + var orderList = overOrderByClauseOpt.ChildNodes[2]; + for (var i = 0; i < orderList.ChildNodes.Count; i++) + { + if (i > 0) { - if (i > 0) - { - _builder.Append(", "); - } - var orderMember = orderList.ChildNodes[i]; - var id = orderMember.ChildNodes[0]; - EvaluateSelectId(id); - var orderDirOpt = orderMember.ChildNodes[1]; - if (orderDirOpt.ChildNodes.Count > 0) - { - _builder.Append(' ').Append(orderDirOpt.ChildNodes[0].Term.Name); - } + _builder.Append(", "); + } + var orderMember = orderList.ChildNodes[i]; + var id = orderMember.ChildNodes[0]; + EvaluateSelectId(id); + var orderDirOpt = orderMember.ChildNodes[1]; + if (orderDirOpt.ChildNodes.Count > 0) + { + _builder.Append(' ').Append(orderDirOpt.ChildNodes[0].Term.Name); } } - - _builder.Append(')'); } - private string EvaluateCteStatement(ParseTreeNode cteStatement) - { - _builder.Append("WITH "); + _builder.Append(')'); + } + + private string EvaluateCteStatement(ParseTreeNode cteStatement) + { + _builder.Append("WITH "); - for (var i = 0; i < cteStatement.ChildNodes[1].ChildNodes.Count; i++) + for (var i = 0; i < cteStatement.ChildNodes[1].ChildNodes.Count; i++) + { + var cte = cteStatement.ChildNodes[1].ChildNodes[i]; + if (i > 0) { - var cte = cteStatement.ChildNodes[1].ChildNodes[i]; - if (i > 0) - { - _builder.Append(", "); - } + _builder.Append(", "); + } - var expressionName = cte.ChildNodes[0].Token.ValueString; - var optionalColumns = cte.ChildNodes[1]; - _builder.Append(expressionName); + var expressionName = cte.ChildNodes[0].Token.ValueString; + var optionalColumns = cte.ChildNodes[1]; + _builder.Append(expressionName); - if (optionalColumns.ChildNodes.Count > 0) - { - var columns = optionalColumns.ChildNodes[0].ChildNodes; - _builder.Append('('); + if (optionalColumns.ChildNodes.Count > 0) + { + var columns = optionalColumns.ChildNodes[0].ChildNodes; + _builder.Append('('); - for (var j = 0; j < columns.Count; j++) + for (var j = 0; j < columns.Count; j++) + { + if (j > 0) { - if (j > 0) - { - _builder.Append(", "); - } - - _builder.Append(columns[j].Token.ValueString); + _builder.Append(", "); } - _builder.Append(')'); + _builder.Append(columns[j].Token.ValueString); } - _builder.Append(" AS ("); - EvaluateStatementList(_builder, cte.ChildNodes[3], false); _builder.Append(')'); } - _builder.Append(' '); - - return _builder.ToString(); + _builder.Append(" AS ("); + EvaluateStatementList(_builder, cte.ChildNodes[3], false); + _builder.Append(')'); } - private void EvaluateStatementList(StringBuilder builder, ParseTreeNode unionStatementList, bool isCteAllowed) + _builder.Append(' '); + + return _builder.ToString(); + } + + private void EvaluateStatementList(StringBuilder builder, ParseTreeNode unionStatementList, bool isCteAllowed) + { + foreach (var unionStatement in unionStatementList.ChildNodes) { - foreach (var unionStatement in unionStatementList.ChildNodes) + var statement = unionStatement.ChildNodes[0]; + var selectStatement = statement.ChildNodes[1]; + var unionClauseOpt = unionStatement.ChildNodes[1]; + if (isCteAllowed) { - var statement = unionStatement.ChildNodes[0]; - var selectStatement = statement.ChildNodes[1]; - var unionClauseOpt = unionStatement.ChildNodes[1]; - if (isCteAllowed) + var cte = statement.ChildNodes[0]; + if (cte.ChildNodes.Count > 0) { - var cte = statement.ChildNodes[0]; - if (cte.ChildNodes.Count > 0) - { - builder.Append(EvaluateCteStatement(cte)); - } + builder.Append(EvaluateCteStatement(cte)); } + } - builder.Append(EvaluateSelectStatement(selectStatement)); + builder.Append(EvaluateSelectStatement(selectStatement)); - for (var i = 0; i < unionClauseOpt.ChildNodes.Count; i++) + for (var i = 0; i < unionClauseOpt.ChildNodes.Count; i++) + { + if (i == 0) { - if (i == 0) - { - builder.Append(' '); - } + builder.Append(' '); + } - var term = unionClauseOpt.ChildNodes[i].Term; + var term = unionClauseOpt.ChildNodes[i].Term; - builder.Append(term).Append(' '); - } + builder.Append(term).Append(' '); } } + } - private enum FormattingModes - { - SelectClause, - FromClause - } + private enum FormattingModes + { + SelectClause, + FromClause + } - private void ClearSelectStatement() - { - _limit = null; - _offset = null; - _select = null; - _from = null; - _where = null; - _having = null; - _groupBy = null; - _orderBy = null; - } + private void ClearSelectStatement() + { + _limit = null; + _offset = null; + _select = null; + _from = null; + _where = null; + _having = null; + _groupBy = null; + _orderBy = null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParserException.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParserException.cs index e7daf5013fd..a4eb4fe7982 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParserException.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlParserException.cs @@ -1,11 +1,10 @@ using System; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +public class SqlParserException : Exception { - public class SqlParserException : Exception + public SqlParserException(string message) : base(message) { - public SqlParserException(string message) : base(message) - { - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs index c5a1a2a3e91..3503afb2548 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/SqlQuerySource.cs @@ -16,105 +16,104 @@ using OrchardCore.Queries.Sql.Models; using YesSql; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +public sealed class SqlQuerySource : IQuerySource { - public sealed class SqlQuerySource : IQuerySource + public const string SourceName = "Sql"; + + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IDbConnectionAccessor _dbConnectionAccessor; + private readonly ISession _session; + private readonly JsonSerializerOptions _jsonSerializerOptions; + private readonly TemplateOptions _templateOptions; + + public SqlQuerySource( + ILiquidTemplateManager liquidTemplateManager, + IDbConnectionAccessor dbConnectionAccessor, + ISession session, + IOptions jsonSerializerOptions, + IOptions templateOptions) { - public const string SourceName = "Sql"; - - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IDbConnectionAccessor _dbConnectionAccessor; - private readonly ISession _session; - private readonly JsonSerializerOptions _jsonSerializerOptions; - private readonly TemplateOptions _templateOptions; - - public SqlQuerySource( - ILiquidTemplateManager liquidTemplateManager, - IDbConnectionAccessor dbConnectionAccessor, - ISession session, - IOptions jsonSerializerOptions, - IOptions templateOptions) - { - _liquidTemplateManager = liquidTemplateManager; - _dbConnectionAccessor = dbConnectionAccessor; - _session = session; - _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; - _templateOptions = templateOptions.Value; - } + _liquidTemplateManager = liquidTemplateManager; + _dbConnectionAccessor = dbConnectionAccessor; + _session = session; + _jsonSerializerOptions = jsonSerializerOptions.Value.SerializerOptions; + _templateOptions = templateOptions.Value; + } - public string Name - => SourceName; + public string Name + => SourceName; - public async Task ExecuteQueryAsync(Query query, IDictionary parameters) - { - var metadata = query.As(); + public async Task ExecuteQueryAsync(Query query, IDictionary parameters) + { + var metadata = query.As(); - var sqlQueryResults = new SQLQueryResults(); + var sqlQueryResults = new SQLQueryResults(); - var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(metadata.Template, NullEncoder.Default, - parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); + var tokenizedQuery = await _liquidTemplateManager.RenderStringAsync(metadata.Template, NullEncoder.Default, + parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); - var dialect = _session.Store.Configuration.SqlDialect; + var dialect = _session.Store.Configuration.SqlDialect; - if (!SqlParser.TryParse(tokenizedQuery, _session.Store.Configuration.Schema, dialect, _session.Store.Configuration.TablePrefix, parameters, out var rawQuery, out var messages)) - { - sqlQueryResults.Items = []; + if (!SqlParser.TryParse(tokenizedQuery, _session.Store.Configuration.Schema, dialect, _session.Store.Configuration.TablePrefix, parameters, out var rawQuery, out var messages)) + { + sqlQueryResults.Items = []; - return sqlQueryResults; - } + return sqlQueryResults; + } - await using var connection = _dbConnectionAccessor.CreateConnection(); + await using var connection = _dbConnectionAccessor.CreateConnection(); - await connection.OpenAsync(); + await connection.OpenAsync(); - if (query.ReturnContentItems) - { - using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); - var queryResult = await connection.QueryAsync(rawQuery, parameters, transaction); + if (query.ReturnContentItems) + { + using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); + var queryResult = await connection.QueryAsync(rawQuery, parameters, transaction); - string column = null; + string column = null; - var documentIds = queryResult.Select(row => - { - var rowDictionary = (IDictionary)row; + var documentIds = queryResult.Select(row => + { + var rowDictionary = (IDictionary)row; - if (column == null) + if (column == null) + { + if (rowDictionary.ContainsKey(nameof(ContentItemIndex.DocumentId))) + { + column = nameof(ContentItemIndex.DocumentId); + } + else { - if (rowDictionary.ContainsKey(nameof(ContentItemIndex.DocumentId))) - { - column = nameof(ContentItemIndex.DocumentId); - } - else - { - column = rowDictionary.FirstOrDefault(kv => kv.Value is long).Key - ?? rowDictionary.First().Key; - } + column = rowDictionary.FirstOrDefault(kv => kv.Value is long).Key + ?? rowDictionary.First().Key; } + } - return rowDictionary.TryGetValue(column, out var documentIdObject) && documentIdObject is long documentId - ? documentId - : 0; - }).ToArray(); + return rowDictionary.TryGetValue(column, out var documentIdObject) && documentIdObject is long documentId + ? documentId + : 0; + }).ToArray(); - sqlQueryResults.Items = await _session.GetAsync(documentIds); + sqlQueryResults.Items = await _session.GetAsync(documentIds); - return sqlQueryResults; - } - else - { - using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); - var queryResults = await connection.QueryAsync(rawQuery, parameters, transaction); + return sqlQueryResults; + } + else + { + using var transaction = await connection.BeginTransactionAsync(_session.Store.Configuration.IsolationLevel); + var queryResults = await connection.QueryAsync(rawQuery, parameters, transaction); - var results = new List(); - foreach (var document in queryResults) - { - results.Add(JObject.FromObject(document, _jsonSerializerOptions)); - } + var results = new List(); + foreach (var document in queryResults) + { + results.Add(JObject.FromObject(document, _jsonSerializerOptions)); + } - sqlQueryResults.Items = results; + sqlQueryResults.Items = results; - return sqlQueryResults; - } + return sqlQueryResults; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Startup.cs index 7c7ffcc1c6a..98fb4e817d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/Startup.cs @@ -8,23 +8,22 @@ using OrchardCore.Queries.Sql.Migrations; using OrchardCore.Security.Permissions; -namespace OrchardCore.Queries.Sql +namespace OrchardCore.Queries.Sql; + +/// +/// These services are registered on the tenant service collection. +/// +[Feature("OrchardCore.Queries.Sql")] +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - [Feature("OrchardCore.Queries.Sql")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, SqlQueryDisplayDriver>(); - services.AddQuerySource(SqlQuerySource.SourceName); + services.AddScoped(); + services.AddScoped, SqlQueryDisplayDriver>(); + services.AddQuerySource(SqlQuerySource.SourceName); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); - } + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/AdminQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/AdminQueryViewModel.cs index f4529747a8c..5eb38c99c58 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/AdminQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/AdminQueryViewModel.cs @@ -2,25 +2,24 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Queries.Sql.ViewModels +namespace OrchardCore.Queries.Sql.ViewModels; + +public class AdminQueryViewModel { - public class AdminQueryViewModel - { - public static string MatchAllQueryBase64 { get; } = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(@"SELECT * FROM ContentItemIndex")); + public static string MatchAllQueryBase64 { get; } = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(@"SELECT * FROM ContentItemIndex")); - public string DecodedQuery { get; set; } - public string Parameters { get; set; } = ""; + public string DecodedQuery { get; set; } + public string Parameters { get; set; } = ""; - [BindNever] - public string RawSql { get; set; } + [BindNever] + public string RawSql { get; set; } - [BindNever] - public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; + [BindNever] + public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; - [BindNever] - public IEnumerable Documents { get; set; } = []; + [BindNever] + public IEnumerable Documents { get; set; } = []; - [BindNever] - public string FactoryName { get; set; } - } + [BindNever] + public string FactoryName { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/SqlQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/SqlQueryViewModel.cs index 02d94cce61e..6d55d8b37fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/SqlQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Sql/ViewModels/SqlQueryViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Queries.Sql.ViewModels +namespace OrchardCore.Queries.Sql.ViewModels; + +public class SqlQueryViewModel { - public class SqlQueryViewModel - { - public string Query { get; set; } + public string Query { get; set; } - public bool ReturnDocuments { get; set; } - } + public bool ReturnDocuments { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs index c601dbe8c8b..83e1bb1d35d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/Startup.cs @@ -15,55 +15,54 @@ using OrchardCore.Scripting; using OrchardCore.Security.Permissions; -namespace OrchardCore.Queries +namespace OrchardCore.Queries; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, QueryDisplayDriver>(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped, QueryDisplayDriver>(); + services.AddScoped(); } +} - [Feature("OrchardCore.Queries.Core")] - public sealed class CoreStartup : StartupBase +[Feature("OrchardCore.Queries.Core")] +public sealed class CoreStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddRecipeExecutionStep(); - services.AddDeployment(); - services.AddSingleton(); + services.AddRecipeExecutionStep(); + services.AddDeployment(); + services.AddSingleton(); - services.Configure(o => + services.Configure(o => + { + o.Scope.SetValue("Queries", new ObjectValue(new LiquidQueriesAccessor())); + o.MemberAccessStrategy.Register(async (obj, name, context) => { - o.Scope.SetValue("Queries", new ObjectValue(new LiquidQueriesAccessor())); - o.MemberAccessStrategy.Register(async (obj, name, context) => - { - var liquidTemplateContext = (LiquidTemplateContext)context; - var queryManager = liquidTemplateContext.Services.GetRequiredService(); + var liquidTemplateContext = (LiquidTemplateContext)context; + var queryManager = liquidTemplateContext.Services.GetRequiredService(); - var query = await queryManager.GetQueryAsync(name); + var query = await queryManager.GetQueryAsync(name); - return FluidValue.Create(query, context.Options); - }); - }) - .AddLiquidFilter("query"); + return FluidValue.Create(query, context.Options); + }); + }) + .AddLiquidFilter("query"); - services.AddScoped(); - } + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment", "OrchardCore.Contents")] - public class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment", "OrchardCore.Contents")] +public class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/EditQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/EditQueryViewModel.cs index cd11cfcf8fe..c91d10cced2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/EditQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/EditQueryViewModel.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Queries.ViewModels +namespace OrchardCore.Queries.ViewModels; + +public class EditQueryViewModel { - public class EditQueryViewModel - { - public int Id { get; set; } - public string Name { get; set; } - public string Source { get; set; } - public string Schema { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public string Source { get; set; } + public string Schema { get; set; } - [BindNever] - public Query Query { get; set; } - } + [BindNever] + public Query Query { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesIndexViewModel.cs index 251c882a3b6..6825bdca581 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueriesIndexViewModel.cs @@ -2,39 +2,38 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Queries.ViewModels +namespace OrchardCore.Queries.ViewModels; + +public class QueriesIndexViewModel +{ + public IList Queries { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + public dynamic Pager { get; set; } + public IEnumerable QuerySourceNames { get; set; } +} + +public class QueryEntry +{ + public Query Query { get; set; } + public bool IsChecked { get; set; } + public dynamic Shape { get; set; } +} + +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + + #endregion Lists to populate +} + +public enum ContentsBulkAction { - public class QueriesIndexViewModel - { - public IList Queries { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - public dynamic Pager { get; set; } - public IEnumerable QuerySourceNames { get; set; } - } - - public class QueryEntry - { - public Query Query { get; set; } - public bool IsChecked { get; set; } - public dynamic Shape { get; set; } - } - - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } - - #region Lists to populate - - [BindNever] - public List ContentsBulkAction { get; set; } - - #endregion Lists to populate - } - - public enum ContentsBulkAction - { - None, - Remove - } + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs index e29bd2621b8..9daeedd2b3c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Queries/ViewModels/QueryBasedContentDeploymentStepViewModel.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Queries.ViewModels +namespace OrchardCore.Queries.ViewModels; + +public class QueryBasedContentDeploymentStepViewModel { - public class QueryBasedContentDeploymentStepViewModel - { - public string QueryName { get; set; } + public string QueryName { get; set; } - public string QueryParameters { get; set; } = "{}"; + public string QueryParameters { get; set; } = "{}"; - public bool ExportAsSetupRecipe { get; set; } + public bool ExportAsSetupRecipe { get; set; } - [BindNever] - public IEnumerable Queries { get; set; } - } + [BindNever] + public IEnumerable Queries { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/AdminMenu.cs index ea1b7a26911..617df9d3917 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/AdminMenu.cs @@ -4,42 +4,41 @@ using OrchardCore.Navigation; using OrchardCore.ReCaptcha.Drivers; -namespace OrchardCore.ReCaptcha +namespace OrchardCore.ReCaptcha; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", ReCaptchaSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", ReCaptchaSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Security"], security => security - .Add(S["Settings"], settings => settings - .Add(S["reCaptcha"], S["reCaptcha"].PrefixPosition(), reCaptcha => reCaptcha - .Permission(Permissions.ManageReCaptchaSettings) - .Action("Index", "Admin", _routeValues) - .LocalNav() - ) + builder + .Add(S["Security"], security => security + .Add(S["Settings"], settings => settings + .Add(S["reCaptcha"], S["reCaptcha"].PrefixPosition(), reCaptcha => reCaptcha + .Permission(Permissions.ManageReCaptchaSettings) + .Action("Index", "Admin", _routeValues) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Drivers/ReCaptchaSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Drivers/ReCaptchaSettingsDisplayDriver.cs index 6dc073a5564..4c31e74bade 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Drivers/ReCaptchaSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Drivers/ReCaptchaSettingsDisplayDriver.cs @@ -9,67 +9,66 @@ using OrchardCore.ReCaptcha.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.ReCaptcha.Drivers +namespace OrchardCore.ReCaptcha.Drivers; + +public sealed class ReCaptchaSettingsDisplayDriver : SiteDisplayDriver { - public sealed class ReCaptchaSettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "recaptcha"; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ReCaptchaSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - public const string GroupId = "recaptcha"; + _shellReleaseManager = shellReleaseManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + protected override string SettingsGroupId + => GroupId; - public ReCaptchaSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + public override async Task EditAsync(ISite site, ReCaptchaSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReCaptchaSettings)) { - _shellReleaseManager = shellReleaseManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => GroupId; + context.AddTenantReloadWarningWrapper(); - public override async Task EditAsync(ISite site, ReCaptchaSettings settings, BuildEditorContext context) + return Initialize("ReCaptchaSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReCaptchaSettings)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); + model.SiteKey = settings.SiteKey; + model.SecretKey = settings.SecretKey; + }).Location("Content") + .OnGroup(SettingsGroupId); + } - return Initialize("ReCaptchaSettings_Edit", model => - { - model.SiteKey = settings.SiteKey; - model.SecretKey = settings.SecretKey; - }).Location("Content") - .OnGroup(SettingsGroupId); - } + public override async Task UpdateAsync(ISite site, ReCaptchaSettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task UpdateAsync(ISite site, ReCaptchaSettings settings, UpdateEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReCaptchaSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReCaptchaSettings)) - { - return null; - } + return null; + } - var model = new ReCaptchaSettingsViewModel(); + var model = new ReCaptchaSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.SiteKey = model.SiteKey?.Trim(); - settings.SecretKey = model.SecretKey?.Trim(); + settings.SiteKey = model.SiteKey?.Trim(); + settings.SecretKey = model.SecretKey?.Trim(); - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Migrations.cs index 7eed71debd2..7d8f01f0a27 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Migrations.cs @@ -3,27 +3,26 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.ReCaptcha.Forms +namespace OrchardCore.ReCaptcha.Forms; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("ReCaptchaPart", part => part - .WithDescription("Provides captcha properties.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("ReCaptchaPart", part => part + .WithDescription("Provides captcha properties.")); - await _contentDefinitionManager.AlterTypeDefinitionAsync("ReCaptcha", type => type - .WithPart("ReCaptchaPart") - .Stereotype("Widget")); + await _contentDefinitionManager.AlterTypeDefinitionAsync("ReCaptcha", type => type + .WithPart("ReCaptchaPart") + .Stereotype("Widget")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPart.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPart.cs index cd4fd2847f9..4027b7a9dc2 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPart.cs @@ -1,8 +1,7 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.ReCaptcha.Forms +namespace OrchardCore.ReCaptcha.Forms; + +public class ReCaptchaPart : ContentPart { - public class ReCaptchaPart : ContentPart - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs index d5acf04d6a4..bba0e70dbca 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartDisplayDriver.cs @@ -4,33 +4,32 @@ using OrchardCore.ReCaptcha.Configuration; using OrchardCore.Settings; -namespace OrchardCore.ReCaptcha.Forms +namespace OrchardCore.ReCaptcha.Forms; + +public sealed class ReCaptchaPartDisplayDriver : ContentPartDisplayDriver { - public sealed class ReCaptchaPartDisplayDriver : ContentPartDisplayDriver - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public ReCaptchaPartDisplayDriver(ISiteService siteService) - { - _siteService = siteService; - } + public ReCaptchaPartDisplayDriver(ISiteService siteService) + { + _siteService = siteService; + } - public override IDisplayResult Display(ReCaptchaPart part, BuildPartDisplayContext context) + public override IDisplayResult Display(ReCaptchaPart part, BuildPartDisplayContext context) + { + return Initialize("ReCaptchaPart", async model => { - return Initialize("ReCaptchaPart", async model => - { - var settings = await _siteService.GetSettingsAsync(); - model.SettingsAreConfigured = settings.IsValid(); - }).Location("Detail", "Content"); - } + var settings = await _siteService.GetSettingsAsync(); + model.SettingsAreConfigured = settings.IsValid(); + }).Location("Detail", "Content"); + } - public override IDisplayResult Edit(ReCaptchaPart part, BuildPartEditorContext context) + public override IDisplayResult Edit(ReCaptchaPart part, BuildPartEditorContext context) + { + return Initialize("ReCaptchaPart_Fields_Edit", async model => { - return Initialize("ReCaptchaPart_Fields_Edit", async model => - { - var settings = await _siteService.GetSettingsAsync(); - model.SettingsAreConfigured = settings.IsValid(); - }); - } + var settings = await _siteService.GetSettingsAsync(); + model.SettingsAreConfigured = settings.IsValid(); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartViewModel.cs index 51ce1a4ca1d..6440930181c 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/ReCaptchaPartViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.ReCaptcha.Forms +namespace OrchardCore.ReCaptcha.Forms; + +public class ReCaptchaPartViewModel { - public class ReCaptchaPartViewModel - { - public bool SettingsAreConfigured { get; set; } - } + public bool SettingsAreConfigured { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Startup.cs index bb3d4d3a1e8..43cec8ea712 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Forms/Startup.cs @@ -5,22 +5,21 @@ using OrchardCore.Data.Migration; using OrchardCore.Modules; -namespace OrchardCore.ReCaptcha.Forms +namespace OrchardCore.ReCaptcha.Forms; + +[RequireFeatures("OrchardCore.Forms", "OrchardCore.ReCaptcha")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Forms", "OrchardCore.ReCaptcha")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - }); + o.MemberAccessStrategy.Register(); + }); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddDataMigration(); - } + services.AddDataMigration(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs index 32274a3b8e1..5d0b1ce96cd 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Startup.cs @@ -13,61 +13,60 @@ using OrchardCore.Users.Events; using OrchardCore.Users.Models; -namespace OrchardCore.ReCaptcha +namespace OrchardCore.ReCaptcha; + +[Feature("OrchardCore.ReCaptcha")] +public sealed class Startup : StartupBase { - [Feature("OrchardCore.ReCaptcha")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddReCaptcha(); + services.AddReCaptcha(); - services.AddScoped, ReCaptchaSettingsDisplayDriver>(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped, ReCaptchaSettingsDisplayDriver>(); + services.AddScoped(); + services.AddScoped(); } +} - [Feature("OrchardCore.ReCaptcha")] - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[Feature("OrchardCore.ReCaptcha")] +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["ReCaptcha settings"], S => S["Exports the ReCaptcha settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["ReCaptcha settings"], S => S["Exports the ReCaptcha settings."]); } +} - [Feature("OrchardCore.ReCaptcha.Users")] - public sealed class UsersStartup : StartupBase +[Feature("OrchardCore.ReCaptcha.Users")] +public sealed class UsersStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, ReCaptchaLoginFormDisplayDriver>(); - } + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, ReCaptchaLoginFormDisplayDriver>(); } +} - [Feature("OrchardCore.ReCaptcha.Users")] - [RequireFeatures(UserConstants.Features.ResetPassword)] - public sealed class UsersResetPasswordStartup : StartupBase +[Feature("OrchardCore.ReCaptcha.Users")] +[RequireFeatures(UserConstants.Features.ResetPassword)] +public sealed class UsersResetPasswordStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped, ReCaptchaForgotPasswordFormDisplayDriver>(); - services.AddScoped, ReCaptchaResetPasswordFormDisplayDriver>(); - } + services.AddScoped, ReCaptchaForgotPasswordFormDisplayDriver>(); + services.AddScoped, ReCaptchaResetPasswordFormDisplayDriver>(); } +} - [Feature("OrchardCore.ReCaptcha.Users")] - [RequireFeatures(UserConstants.Features.UserRegistration)] - public sealed class UsersRegistrationStartup : StartupBase +[Feature("OrchardCore.ReCaptcha.Users")] +[RequireFeatures(UserConstants.Features.UserRegistration)] +public sealed class UsersRegistrationStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped, RegisterUserFormDisplayDriver>(); - } + services.AddScoped, RegisterUserFormDisplayDriver>(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/LoginFormEventEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/LoginFormEventEventHandler.cs index 1509864b5c2..a5ffe17f757 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/LoginFormEventEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/LoginFormEventEventHandler.cs @@ -4,43 +4,42 @@ using OrchardCore.Users; using OrchardCore.Users.Events; -namespace OrchardCore.ReCaptcha.Users.Handlers +namespace OrchardCore.ReCaptcha.Users.Handlers; + +public class LoginFormEventEventHandler : ILoginFormEvent { - public class LoginFormEventEventHandler : ILoginFormEvent - { - private readonly ReCaptchaService _reCaptchaService; + private readonly ReCaptchaService _reCaptchaService; - public LoginFormEventEventHandler(ReCaptchaService reCaptchaService) - { - _reCaptchaService = reCaptchaService; - } + public LoginFormEventEventHandler(ReCaptchaService reCaptchaService) + { + _reCaptchaService = reCaptchaService; + } - public Task IsLockedOutAsync(IUser user) => Task.CompletedTask; + public Task IsLockedOutAsync(IUser user) => Task.CompletedTask; - public Task LoggedInAsync(IUser user) - { - _reCaptchaService.ThisIsAHuman(); - return Task.CompletedTask; - } + public Task LoggedInAsync(IUser user) + { + _reCaptchaService.ThisIsAHuman(); + return Task.CompletedTask; + } - public async Task LoggingInAsync(string userName, Action reportError) + public async Task LoggingInAsync(string userName, Action reportError) + { + if (_reCaptchaService.IsThisARobot()) { - if (_reCaptchaService.IsThisARobot()) - { - await _reCaptchaService.ValidateCaptchaAsync(reportError); - } + await _reCaptchaService.ValidateCaptchaAsync(reportError); } + } - public Task LoggingInFailedAsync(string userName) - { - _reCaptchaService.MaybeThisIsARobot(); - return Task.CompletedTask; - } + public Task LoggingInFailedAsync(string userName) + { + _reCaptchaService.MaybeThisIsARobot(); + return Task.CompletedTask; + } - public Task LoggingInFailedAsync(IUser user) - { - _reCaptchaService.MaybeThisIsARobot(); - return Task.CompletedTask; - } + public Task LoggingInFailedAsync(IUser user) + { + _reCaptchaService.MaybeThisIsARobot(); + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/PasswordRecoveryFormEventEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/PasswordRecoveryFormEventEventHandler.cs index b5dfc9e7d86..7535e4a31f7 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/PasswordRecoveryFormEventEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/PasswordRecoveryFormEventEventHandler.cs @@ -3,35 +3,34 @@ using OrchardCore.ReCaptcha.Services; using OrchardCore.Users.Events; -namespace OrchardCore.ReCaptcha.Users.Handlers +namespace OrchardCore.ReCaptcha.Users.Handlers; + +public class PasswordRecoveryFormEventEventHandler : IPasswordRecoveryFormEvents { - public class PasswordRecoveryFormEventEventHandler : IPasswordRecoveryFormEvents - { - private readonly ReCaptchaService _recaptchaService; + private readonly ReCaptchaService _recaptchaService; - public PasswordRecoveryFormEventEventHandler(ReCaptchaService recaptchaService) - { - _recaptchaService = recaptchaService; - } + public PasswordRecoveryFormEventEventHandler(ReCaptchaService recaptchaService) + { + _recaptchaService = recaptchaService; + } - public Task RecoveringPasswordAsync(Action reportError) - { - return _recaptchaService.ValidateCaptchaAsync(reportError); - } + public Task RecoveringPasswordAsync(Action reportError) + { + return _recaptchaService.ValidateCaptchaAsync(reportError); + } - public Task PasswordResetAsync(PasswordRecoveryContext context) - { - return Task.CompletedTask; - } + public Task PasswordResetAsync(PasswordRecoveryContext context) + { + return Task.CompletedTask; + } - public Task ResettingPasswordAsync(Action reportError) - { - return _recaptchaService.ValidateCaptchaAsync(reportError); - } + public Task ResettingPasswordAsync(Action reportError) + { + return _recaptchaService.ValidateCaptchaAsync(reportError); + } - public Task PasswordRecoveredAsync(PasswordRecoveryContext context) - { - return Task.CompletedTask; - } + public Task PasswordRecoveredAsync(PasswordRecoveryContext context) + { + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/RegistrationFormEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/RegistrationFormEventHandler.cs index 3d8eaae9b45..a4a33519bc2 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/RegistrationFormEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Users/Handlers/RegistrationFormEventHandler.cs @@ -4,25 +4,24 @@ using OrchardCore.Users; using OrchardCore.Users.Events; -namespace OrchardCore.ReCaptcha.Users.Handlers +namespace OrchardCore.ReCaptcha.Users.Handlers; + +public class RegistrationFormEventHandler : IRegistrationFormEvents { - public class RegistrationFormEventHandler : IRegistrationFormEvents - { - private readonly ReCaptchaService _reCaptchaService; + private readonly ReCaptchaService _reCaptchaService; - public RegistrationFormEventHandler(ReCaptchaService recaptchaService) - { - _reCaptchaService = recaptchaService; - } + public RegistrationFormEventHandler(ReCaptchaService recaptchaService) + { + _reCaptchaService = recaptchaService; + } - public Task RegisteredAsync(IUser user) - { - return Task.CompletedTask; - } + public Task RegisteredAsync(IUser user) + { + return Task.CompletedTask; + } - public Task RegistrationValidationAsync(Action reportError) - { - return _reCaptchaService.ValidateCaptchaAsync(reportError); - } + public Task RegistrationValidationAsync(Action reportError) + { + return _reCaptchaService.ValidateCaptchaAsync(reportError); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/ViewModels/ReCaptchaSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/ViewModels/ReCaptchaSettingsViewModel.cs index a1802f0d2d1..ae91239f56d 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/ViewModels/ReCaptchaSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/ViewModels/ReCaptchaSettingsViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.ReCaptcha.ViewModels +namespace OrchardCore.ReCaptcha.ViewModels; + +public class ReCaptchaSettingsViewModel { - public class ReCaptchaSettingsViewModel - { - public string SiteKey { get; set; } + public string SiteKey { get; set; } - public string SecretKey { get; set; } - } + public string SecretKey { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/Startup.cs index b157a02e4f6..5637f483997 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/Startup.cs @@ -2,14 +2,13 @@ using OrchardCore.Modules; using OrchardCore.Workflows.Helpers; -namespace OrchardCore.ReCaptcha.Workflows +namespace OrchardCore.ReCaptcha.Workflows; + +[RequireFeatures("OrchardCore.Workflows", "OrchardCore.ReCaptcha")] +public sealed class Startup : StartupBase { - [RequireFeatures("OrchardCore.Workflows", "OrchardCore.ReCaptcha")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddActivity(); - } + services.AddActivity(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTask.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTask.cs index 5d5167c1e63..2e8652d41ad 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTask.cs @@ -7,49 +7,48 @@ using OrchardCore.Workflows.Activities; using OrchardCore.Workflows.Models; -namespace OrchardCore.ReCaptcha.Workflows +namespace OrchardCore.ReCaptcha.Workflows; + +public class ValidateReCaptchaTask : TaskActivity { - public class ValidateReCaptchaTask : TaskActivity + private readonly ReCaptchaService _reCaptchaService; + private readonly IUpdateModelAccessor _updateModelAccessor; + protected readonly IStringLocalizer S; + + public ValidateReCaptchaTask( + ReCaptchaService reCaptchaService, + IUpdateModelAccessor updateModelAccessor, + IStringLocalizer localizer + ) { - private readonly ReCaptchaService _reCaptchaService; - private readonly IUpdateModelAccessor _updateModelAccessor; - protected readonly IStringLocalizer S; - - public ValidateReCaptchaTask( - ReCaptchaService reCaptchaService, - IUpdateModelAccessor updateModelAccessor, - IStringLocalizer localizer - ) - { - _reCaptchaService = reCaptchaService; - _updateModelAccessor = updateModelAccessor; - S = localizer; - } + _reCaptchaService = reCaptchaService; + _updateModelAccessor = updateModelAccessor; + S = localizer; + } - public override LocalizedString DisplayText => S["Validate ReCaptcha Task"]; + public override LocalizedString DisplayText => S["Validate ReCaptcha Task"]; - public override LocalizedString Category => S["Validation"]; + public override LocalizedString Category => S["Validation"]; - public override bool HasEditor => false; + public override bool HasEditor => false; - public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - return Outcomes(S["Done"], S["Valid"], S["Invalid"]); - } + public override IEnumerable GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + return Outcomes(S["Done"], S["Valid"], S["Invalid"]); + } - public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) - { - var outcome = "Valid"; + public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) + { + var outcome = "Valid"; - await _reCaptchaService.ValidateCaptchaAsync((key, error) => - { - var updater = _updateModelAccessor.ModelUpdater; - outcome = "Invalid"; + await _reCaptchaService.ValidateCaptchaAsync((key, error) => + { + var updater = _updateModelAccessor.ModelUpdater; + outcome = "Invalid"; - updater?.ModelState.TryAddModelError(Constants.ReCaptchaServerResponseHeaderName, S["Captcha validation failed. Try again."]); - }); + updater?.ModelState.TryAddModelError(Constants.ReCaptchaServerResponseHeaderName, S["Captcha validation failed. Try again."]); + }); - return Outcomes("Done", outcome); - } + return Outcomes("Done", outcome); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTaskDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTaskDisplayDriver.cs index f2db8d400b1..698532ef544 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTaskDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReCaptcha/Workflows/ValidateReCaptchaTaskDisplayDriver.cs @@ -1,8 +1,7 @@ using OrchardCore.Workflows.Display; -namespace OrchardCore.ReCaptcha.Workflows +namespace OrchardCore.ReCaptcha.Workflows; + +public sealed class ValidateReCaptchaTaskDisplayDriver : ActivityDisplayDriver { - public sealed class ValidateReCaptchaTaskDisplayDriver : ActivityDisplayDriver - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/AdminMenu.cs index b56a6b82ae8..a724373258f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/AdminMenu.cs @@ -3,36 +3,35 @@ using OrchardCore.Navigation; using OrchardCore.Security; -namespace OrchardCore.Recipes +namespace OrchardCore.Recipes; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Recipes"], S["Recipes"].PrefixPosition(), recipes => recipes - .AddClass("recipes") - .Id("recipes") - .Permission(StandardPermissions.SiteOwner) - .Action("Index", "Admin", "OrchardCore.Recipes") - .LocalNav() - ) - ); + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Recipes"], S["Recipes"].PrefixPosition(), recipes => recipes + .AddClass("recipes") + .Id("recipes") + .Permission(StandardPermissions.SiteOwner) + .Action("Index", "Admin", "OrchardCore.Recipes") + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/Commands/RecipesCommands.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/Commands/RecipesCommands.cs index 11f8b2d4deb..a2abdb0107d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/Commands/RecipesCommands.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/Commands/RecipesCommands.cs @@ -5,45 +5,44 @@ using OrchardCore.Environment.Commands; using OrchardCore.Recipes.Services; -namespace OrchardCore.Recipes.Commands +namespace OrchardCore.Recipes.Commands; + +public class RecipesCommands : DefaultCommandHandler { - public class RecipesCommands : DefaultCommandHandler + private readonly IEnumerable _recipeHarvesters; + + public RecipesCommands( + IEnumerable recipeHarvesters, + IStringLocalizer localizer) : base(localizer) { - private readonly IEnumerable _recipeHarvesters; + _recipeHarvesters = recipeHarvesters; + } - public RecipesCommands( - IEnumerable recipeHarvesters, - IStringLocalizer localizer) : base(localizer) - { - _recipeHarvesters = recipeHarvesters; - } + [CommandHelp("recipes harvest", "\tDisplays a list of available recipes.")] + [CommandName("recipes harvest")] + public async Task Harvest() + { + var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); + var recipes = recipeCollections.SelectMany(x => x).ToArray(); - [CommandHelp("recipes harvest", "\tDisplays a list of available recipes.")] - [CommandName("recipes harvest")] - public async Task Harvest() + if (recipes.Length == 0) { - var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); - var recipes = recipeCollections.SelectMany(x => x).ToArray(); - - if (recipes.Length == 0) - { - await Context.Output.WriteLineAsync(S["No recipes found."]); - return; - } + await Context.Output.WriteLineAsync(S["No recipes found."]); + return; + } - await Context.Output.WriteLineAsync(S["List of available recipes"]); - await Context.Output.WriteLineAsync("--------------------------"); - await Context.Output.WriteLineAsync(); + await Context.Output.WriteLineAsync(S["List of available recipes"]); + await Context.Output.WriteLineAsync("--------------------------"); + await Context.Output.WriteLineAsync(); - foreach (var recipe in recipes) - { - await Context.Output.WriteLineAsync(S["Recipe: {0}", recipe.Name]); - await Context.Output.WriteLineAsync(S[" Version: {0}", recipe.Version]); - await Context.Output.WriteLineAsync(S[" Tags: {0}", string.Join(",", recipe.Tags)]); - await Context.Output.WriteLineAsync(S[" Description: {0}", recipe.Description]); - await Context.Output.WriteLineAsync(S[" Author: {0}", recipe.Author]); - await Context.Output.WriteLineAsync(S[" Website: {0}", recipe.WebSite]); - } + foreach (var recipe in recipes) + { + await Context.Output.WriteLineAsync(S["Recipe: {0}", recipe.Name]); + await Context.Output.WriteLineAsync(S[" Version: {0}", recipe.Version]); + await Context.Output.WriteLineAsync(S[" Tags: {0}", string.Join(",", recipe.Tags)]); + await Context.Output.WriteLineAsync(S[" Description: {0}", recipe.Description]); + await Context.Output.WriteLineAsync(S[" Author: {0}", recipe.Author]); + await Context.Output.WriteLineAsync(S[" Website: {0}", recipe.WebSite]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/Controllers/AdminController.cs index 401a534ce9c..e416e007bcf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/Controllers/AdminController.cs @@ -18,133 +18,132 @@ using OrchardCore.Recipes.ViewModels; using OrchardCore.Security; -namespace OrchardCore.Recipes.Controllers +namespace OrchardCore.Recipes.Controllers; + +[Admin("Recipes/{action}", "Recipes{action}")] +public class AdminController : Controller { - [Admin("Recipes/{action}", "Recipes{action}")] - public class AdminController : Controller + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IShellFeaturesManager _shellFeaturesManager; + private readonly IAuthorizationService _authorizationService; + private readonly IEnumerable _recipeHarvesters; + private readonly IRecipeExecutor _recipeExecutor; + private readonly IEnumerable _environmentProviders; + private readonly INotifier _notifier; + private readonly ILogger _logger; + + protected readonly IHtmlLocalizer H; + protected readonly IStringLocalizer S; + + public AdminController( + IShellHost shellHost, + ShellSettings shellSettings, + IShellFeaturesManager shellFeaturesManager, + IAuthorizationService authorizationService, + IEnumerable recipeHarvesters, + IRecipeExecutor recipeExecutor, + IEnumerable environmentProviders, + INotifier notifier, + ILogger logger, + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer) { - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IShellFeaturesManager _shellFeaturesManager; - private readonly IAuthorizationService _authorizationService; - private readonly IEnumerable _recipeHarvesters; - private readonly IRecipeExecutor _recipeExecutor; - private readonly IEnumerable _environmentProviders; - private readonly INotifier _notifier; - private readonly ILogger _logger; - - protected readonly IHtmlLocalizer H; - protected readonly IStringLocalizer S; - - public AdminController( - IShellHost shellHost, - ShellSettings shellSettings, - IShellFeaturesManager shellFeaturesManager, - IAuthorizationService authorizationService, - IEnumerable recipeHarvesters, - IRecipeExecutor recipeExecutor, - IEnumerable environmentProviders, - INotifier notifier, - ILogger logger, - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer) - { - _shellHost = shellHost; - _shellSettings = shellSettings; - _shellFeaturesManager = shellFeaturesManager; - _authorizationService = authorizationService; - _recipeHarvesters = recipeHarvesters; - _recipeExecutor = recipeExecutor; - _environmentProviders = environmentProviders; - _notifier = notifier; - _logger = logger; - H = htmlLocalizer; - S = stringLocalizer; - } + _shellHost = shellHost; + _shellSettings = shellSettings; + _shellFeaturesManager = shellFeaturesManager; + _authorizationService = authorizationService; + _recipeHarvesters = recipeHarvesters; + _recipeExecutor = recipeExecutor; + _environmentProviders = environmentProviders; + _notifier = notifier; + _logger = logger; + H = htmlLocalizer; + S = stringLocalizer; + } - [Admin("Recipes", "Recipes")] - public async Task Index() + [Admin("Recipes", "Recipes")] + public async Task Index() + { + if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) { - if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) - { - return Forbid(); - } - - var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); - var recipes = await GetRecipesAsync(features); - - var model = recipes.Select(recipe => new RecipeViewModel - { - Name = recipe.Name, - DisplayName = recipe.DisplayName, - FileName = recipe.RecipeFileInfo.Name, - BasePath = recipe.BasePath, - Tags = recipe.Tags, - IsSetupRecipe = recipe.IsSetupRecipe, - Feature = features.FirstOrDefault(f => recipe.BasePath.Contains(f.Extension.SubPath))?.Name ?? "Application", - Description = recipe.Description - }).ToArray(); - - return View(model); + return Forbid(); } - [HttpPost] - public async Task Execute(string basePath, string fileName) - { - if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) - { - return Forbid(); - } - - var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); - var recipes = await GetRecipesAsync(features); + var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); + var recipes = await GetRecipesAsync(features); - var recipe = recipes.FirstOrDefault(c => c.RecipeFileInfo.Name == fileName && c.BasePath == basePath); + var model = recipes.Select(recipe => new RecipeViewModel + { + Name = recipe.Name, + DisplayName = recipe.DisplayName, + FileName = recipe.RecipeFileInfo.Name, + BasePath = recipe.BasePath, + Tags = recipe.Tags, + IsSetupRecipe = recipe.IsSetupRecipe, + Feature = features.FirstOrDefault(f => recipe.BasePath.Contains(f.Extension.SubPath))?.Name ?? "Application", + Description = recipe.Description + }).ToArray(); + + return View(model); + } - if (recipe == null) - { - await _notifier.ErrorAsync(H["Recipe was not found."]); - return RedirectToAction(nameof(Index)); - } + [HttpPost] + public async Task Execute(string basePath, string fileName) + { + if (!await _authorizationService.AuthorizeAsync(User, StandardPermissions.SiteOwner)) + { + return Forbid(); + } - var environment = new Dictionary(); - await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); + var features = await _shellFeaturesManager.GetAvailableFeaturesAsync(); + var recipes = await GetRecipesAsync(features); - try - { - var executionId = Guid.NewGuid().ToString("n"); + var recipe = recipes.FirstOrDefault(c => c.RecipeFileInfo.Name == fileName && c.BasePath == basePath); - await _recipeExecutor.ExecuteAsync(executionId, recipe, environment, CancellationToken.None); + if (recipe == null) + { + await _notifier.ErrorAsync(H["Recipe was not found."]); + return RedirectToAction(nameof(Index)); + } - await _shellHost.ReleaseShellContextAsync(_shellSettings); + var environment = new Dictionary(); + await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); - await _notifier.SuccessAsync(H["The recipe '{0}' has been run successfully.", recipe.DisplayName]); - } - catch (RecipeExecutionException e) - { - _logger.LogError(e, "Unable to import a recipe file."); + try + { + var executionId = Guid.NewGuid().ToString("n"); - await _notifier.ErrorAsync(H["The recipe '{0}' failed to run do to the following errors: {1}", recipe.DisplayName, string.Join(' ', e.StepResult.Errors)]); - } - catch (Exception e) - { - _logger.LogError(e, "Unable to import a recipe file."); + await _recipeExecutor.ExecuteAsync(executionId, recipe, environment, CancellationToken.None); - await _notifier.ErrorAsync(H["Unexpected error occurred while running the '{0}' recipe.", recipe.DisplayName]); - } + await _shellHost.ReleaseShellContextAsync(_shellSettings); - return RedirectToAction(nameof(Index)); + await _notifier.SuccessAsync(H["The recipe '{0}' has been run successfully.", recipe.DisplayName]); } + catch (RecipeExecutionException e) + { + _logger.LogError(e, "Unable to import a recipe file."); - private async Task> GetRecipesAsync(IEnumerable features) + await _notifier.ErrorAsync(H["The recipe '{0}' failed to run do to the following errors: {1}", recipe.DisplayName, string.Join(' ', e.StepResult.Errors)]); + } + catch (Exception e) { - var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); - var recipes = recipeCollections.SelectMany(x => x) - .Where(r => !r.IsSetupRecipe && - (r.Tags == null || !r.Tags.Contains("hidden", StringComparer.InvariantCultureIgnoreCase)) && - features.Any(f => r.BasePath != null && f.Extension?.SubPath != null && r.BasePath.Contains(f.Extension.SubPath, StringComparison.OrdinalIgnoreCase))); + _logger.LogError(e, "Unable to import a recipe file."); - return recipes; + await _notifier.ErrorAsync(H["Unexpected error occurred while running the '{0}' recipe.", recipe.DisplayName]); } + + return RedirectToAction(nameof(Index)); + } + + private async Task> GetRecipesAsync(IEnumerable features) + { + var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(x => x.HarvestRecipesAsync())); + var recipes = recipeCollections.SelectMany(x => x) + .Where(r => !r.IsSetupRecipe && + (r.Tags == null || !r.Tags.Contains("hidden", StringComparer.InvariantCultureIgnoreCase)) && + features.Any(f => r.BasePath != null && f.Extension?.SubPath != null && r.BasePath.Contains(f.Extension.SubPath, StringComparison.OrdinalIgnoreCase))); + + return recipes; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/CommandStep.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/CommandStep.cs index 2e260ecce5a..661f7f6db5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/CommandStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/CommandStep.cs @@ -8,58 +8,57 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Recipes.RecipeSteps +namespace OrchardCore.Recipes.RecipeSteps; + +/// +/// This recipe step executes a set of commands. +/// +public sealed class CommandStep : IRecipeStepHandler { - /// - /// This recipe step executes a set of commands. - /// - public sealed class CommandStep : IRecipeStepHandler + private readonly ICommandManager _commandManager; + private readonly ICommandParser _commandParser; + private readonly ICommandParametersParser _commandParameterParser; + private readonly ILogger _logger; + + public CommandStep(ICommandManager commandManager, + ICommandParser commandParser, + ICommandParametersParser commandParameterParser, + ILogger logger) { - private readonly ICommandManager _commandManager; - private readonly ICommandParser _commandParser; - private readonly ICommandParametersParser _commandParameterParser; - private readonly ILogger _logger; + _commandManager = commandManager; + _commandParser = commandParser; + _commandParameterParser = commandParameterParser; + _logger = logger; + } - public CommandStep(ICommandManager commandManager, - ICommandParser commandParser, - ICommandParametersParser commandParameterParser, - ILogger logger) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Command", StringComparison.OrdinalIgnoreCase)) { - _commandManager = commandManager; - _commandParser = commandParser; - _commandParameterParser = commandParameterParser; - _logger = logger; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "Command", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var step = context.Step.ToObject(); + var step = context.Step.ToObject(); - foreach (var command in step.Commands) + foreach (var command in step.Commands) + { + await using (var output = new ZStringWriter()) { - await using (var output = new ZStringWriter()) - { - _logger.LogInformation("Executing command: {Command}", command); - - var commandParameters = _commandParameterParser.Parse(_commandParser.Parse(command)); - commandParameters.Output = output; - await _commandManager.ExecuteAsync(commandParameters); + _logger.LogInformation("Executing command: {Command}", command); - _logger.LogInformation("Command executed with output: {CommandOutput}", output); - } + var commandParameters = _commandParameterParser.Parse(_commandParser.Parse(command)); + commandParameters.Output = output; + await _commandManager.ExecuteAsync(commandParameters); - _logger.LogInformation("Executed command: {Command}", command); + _logger.LogInformation("Command executed with output: {CommandOutput}", output); } - } - private sealed class CommandStepModel - { - public string[] Commands { get; set; } + _logger.LogInformation("Executed command: {Command}", command); } } + + private sealed class CommandStepModel + { + public string[] Commands { get; set; } + } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/RecipesStep.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/RecipesStep.cs index dce986da558..f06590ec331 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/RecipesStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/RecipeSteps/RecipesStep.cs @@ -7,63 +7,62 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Recipes.RecipeSteps +namespace OrchardCore.Recipes.RecipeSteps; + +/// +/// This recipe step executes a set of external recipes. +/// +public sealed class RecipesStep : IRecipeStepHandler { - /// - /// This recipe step executes a set of external recipes. - /// - public sealed class RecipesStep : IRecipeStepHandler - { - private readonly IEnumerable _recipeHarvesters; + private readonly IEnumerable _recipeHarvesters; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public RecipesStep( - IEnumerable recipeHarvesters, - IStringLocalizer stringLocalizer) - { - _recipeHarvesters = recipeHarvesters; - S = stringLocalizer; - } + public RecipesStep( + IEnumerable recipeHarvesters, + IStringLocalizer stringLocalizer) + { + _recipeHarvesters = recipeHarvesters; + S = stringLocalizer; + } - public async Task ExecuteAsync(RecipeExecutionContext context) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Recipes", StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(context.Name, "Recipes", StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - var step = context.Step.ToObject(); + var step = context.Step.ToObject(); - var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(harvester => harvester.HarvestRecipesAsync())); - var recipes = recipeCollections.SelectMany(recipe => recipe).ToDictionary(recipe => recipe.Name); + var recipeCollections = await Task.WhenAll(_recipeHarvesters.Select(harvester => harvester.HarvestRecipesAsync())); + var recipes = recipeCollections.SelectMany(recipe => recipe).ToDictionary(recipe => recipe.Name); - var innerRecipes = new List(); - foreach (var recipe in step.Values) + var innerRecipes = new List(); + foreach (var recipe in step.Values) + { + if (!recipes.TryGetValue(recipe.Name, out var value)) { - if (!recipes.TryGetValue(recipe.Name, out var value)) - { - context.Errors.Add(S["No recipe named '{0}' was found.", recipe.Name]); + context.Errors.Add(S["No recipe named '{0}' was found.", recipe.Name]); - continue; - } - - innerRecipes.Add(value); + continue; } - context.InnerRecipes = innerRecipes; + innerRecipes.Add(value); } - private sealed class InternalStep - { - public InternalStepValue[] Values { get; set; } - } + context.InnerRecipes = innerRecipes; + } - private sealed class InternalStepValue - { - public string ExecutionId { get; set; } + private sealed class InternalStep + { + public InternalStepValue[] Values { get; set; } + } - public string Name { get; set; } - } + private sealed class InternalStepValue + { + public string ExecutionId { get; set; } + + public string Name { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/Services/RecipeDeploymentTargetHandler.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/Services/RecipeDeploymentTargetHandler.cs index cdd31f5ffa4..eada53d96d2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/Services/RecipeDeploymentTargetHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/Services/RecipeDeploymentTargetHandler.cs @@ -10,45 +10,44 @@ using OrchardCore.Modules; using OrchardCore.Recipes.Models; -namespace OrchardCore.Recipes.Services +namespace OrchardCore.Recipes.Services; + +public class RecipeDeploymentTargetHandler : IDeploymentTargetHandler { - public class RecipeDeploymentTargetHandler : IDeploymentTargetHandler - { - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly IRecipeExecutor _recipeExecutor; - private readonly IEnumerable _environmentProviders; - private readonly ILogger _logger; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly IRecipeExecutor _recipeExecutor; + private readonly IEnumerable _environmentProviders; + private readonly ILogger _logger; - public RecipeDeploymentTargetHandler(IShellHost shellHost, - ShellSettings shellSettings, - IRecipeExecutor recipeExecutor, - IEnumerable environmentProviders, - ILogger logger) - { - _shellHost = shellHost; - _shellSettings = shellSettings; - _recipeExecutor = recipeExecutor; - _environmentProviders = environmentProviders; - _logger = logger; - } + public RecipeDeploymentTargetHandler(IShellHost shellHost, + ShellSettings shellSettings, + IRecipeExecutor recipeExecutor, + IEnumerable environmentProviders, + ILogger logger) + { + _shellHost = shellHost; + _shellSettings = shellSettings; + _recipeExecutor = recipeExecutor; + _environmentProviders = environmentProviders; + _logger = logger; + } - public async Task ImportFromFileAsync(IFileProvider fileProvider) + public async Task ImportFromFileAsync(IFileProvider fileProvider) + { + var executionId = Guid.NewGuid().ToString("n"); + var recipeDescriptor = new RecipeDescriptor { - var executionId = Guid.NewGuid().ToString("n"); - var recipeDescriptor = new RecipeDescriptor - { - FileProvider = fileProvider, - BasePath = "", - RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json") - }; + FileProvider = fileProvider, + BasePath = "", + RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json") + }; - var environment = new Dictionary(); - await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); + var environment = new Dictionary(); + await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger); - await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, environment, CancellationToken.None); + await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, environment, CancellationToken.None); - await _shellHost.ReleaseShellContextAsync(_shellSettings); - } + await _shellHost.ReleaseShellContextAsync(_shellSettings); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/Startup.cs index e35aa0e575a..6342412b4f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/Startup.cs @@ -5,30 +5,29 @@ using OrchardCore.Recipes.RecipeSteps; using OrchardCore.Recipes.Services; -namespace OrchardCore.Recipes +namespace OrchardCore.Recipes; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); - services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.AddDeploymentTargetHandler(); - } + services.AddDeploymentTargetHandler(); } +} - [Feature("OrchardCore.Recipes.Core")] - public sealed class RecipesCoreStartup : StartupBase +[Feature("OrchardCore.Recipes.Core")] +public sealed class RecipesCoreStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddRecipes(); - } + services.AddRecipes(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Recipes/ViewModels/RecipeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Recipes/ViewModels/RecipeViewModel.cs index 393417b14d1..c9d481fb2b3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Recipes/ViewModels/RecipeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Recipes/ViewModels/RecipeViewModel.cs @@ -1,14 +1,13 @@ -namespace OrchardCore.Recipes.ViewModels +namespace OrchardCore.Recipes.ViewModels; + +public class RecipeViewModel { - public class RecipeViewModel - { - public string FileName { get; set; } - public string Name { get; set; } - public string DisplayName { get; set; } - public string[] Tags { get; set; } - public bool IsSetupRecipe { get; set; } - public string Description { get; set; } - public string BasePath { get; set; } - public string Feature { get; set; } - } + public string FileName { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public string[] Tags { get; set; } + public bool IsSetupRecipe { get; set; } + public string Description { get; set; } + public string BasePath { get; set; } + public string Feature { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisCacheOptionsSetup.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisCacheOptionsSetup.cs index 7452103046b..c90cd0da7e5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisCacheOptionsSetup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisCacheOptionsSetup.cs @@ -2,33 +2,32 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; -namespace OrchardCore.Redis.Options +namespace OrchardCore.Redis.Options; + +public sealed class RedisCacheOptionsSetup : IConfigureOptions { - public sealed class RedisCacheOptionsSetup : IConfigureOptions - { - private readonly IRedisService _redis; - private readonly string _tenant; + private readonly IRedisService _redis; + private readonly string _tenant; - public RedisCacheOptionsSetup(IRedisService redis, ShellSettings shellSettings) - { - _redis = redis; - _tenant = shellSettings.Name; - } + public RedisCacheOptionsSetup(IRedisService redis, ShellSettings shellSettings) + { + _redis = redis; + _tenant = shellSettings.Name; + } - public void Configure(RedisCacheOptions options) + public void Configure(RedisCacheOptions options) + { + var redis = _redis; + options.ConnectionMultiplexerFactory = async () => { - var redis = _redis; - options.ConnectionMultiplexerFactory = async () => + if (redis.Connection == null) { - if (redis.Connection == null) - { - await redis.ConnectAsync(); - } + await redis.ConnectAsync(); + } - return redis.Connection; - }; + return redis.Connection; + }; - options.InstanceName = $"{redis.InstancePrefix}{_tenant}"; - } + options.InstanceName = $"{redis.InstancePrefix}{_tenant}"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisKeyManagementOptionsSetup.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisKeyManagementOptionsSetup.cs index f495363410b..44a77b118c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisKeyManagementOptionsSetup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Options/RedisKeyManagementOptionsSetup.cs @@ -3,32 +3,31 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; -namespace OrchardCore.Redis.Options +namespace OrchardCore.Redis.Options; + +public sealed class RedisKeyManagementOptionsSetup : IConfigureOptions { - public sealed class RedisKeyManagementOptionsSetup : IConfigureOptions - { - private readonly IRedisService _redis; - private readonly string _tenant; + private readonly IRedisService _redis; + private readonly string _tenant; - public RedisKeyManagementOptionsSetup(IRedisService redis, ShellSettings shellSettings) - { - _redis = redis; - _tenant = shellSettings.Name; - } + public RedisKeyManagementOptionsSetup(IRedisService redis, ShellSettings shellSettings) + { + _redis = redis; + _tenant = shellSettings.Name; + } - public void Configure(KeyManagementOptions options) + public void Configure(KeyManagementOptions options) + { + var redis = _redis; + options.XmlRepository = new RedisXmlRepository(() => { - var redis = _redis; - options.XmlRepository = new RedisXmlRepository(() => + if (redis.Database == null) { - if (redis.Database == null) - { - redis.ConnectAsync().GetAwaiter().GetResult(); - } + redis.ConnectAsync().GetAwaiter().GetResult(); + } - return redis.Database; - }, - $"({redis.InstancePrefix}{_tenant}:DataProtection-Keys"); - } + return redis.Database; + }, + $"({redis.InstancePrefix}{_tenant}:DataProtection-Keys"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisBus.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisBus.cs index 58a7bec351c..186b3c7b34f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisBus.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisBus.cs @@ -7,79 +7,78 @@ using OrchardCore.Environment.Shell; using StackExchange.Redis; -namespace OrchardCore.Redis.Services +namespace OrchardCore.Redis.Services; + +public class RedisBus : IMessageBus { - public class RedisBus : IMessageBus - { - private readonly IRedisService _redis; - private readonly string _hostName; - private readonly string _channelPrefix; - private readonly string _messagePrefix; - private readonly ILogger _logger; + private readonly IRedisService _redis; + private readonly string _hostName; + private readonly string _channelPrefix; + private readonly string _messagePrefix; + private readonly ILogger _logger; - public RedisBus(IRedisService redis, ShellSettings shellSettings, ILogger logger) - { - _redis = redis; - _hostName = Dns.GetHostName() + ':' + System.Environment.ProcessId; - _channelPrefix = redis.InstancePrefix + shellSettings.Name + ':'; - _messagePrefix = _hostName + '/'; - _logger = logger; - } + public RedisBus(IRedisService redis, ShellSettings shellSettings, ILogger logger) + { + _redis = redis; + _hostName = Dns.GetHostName() + ':' + System.Environment.ProcessId; + _channelPrefix = redis.InstancePrefix + shellSettings.Name + ':'; + _messagePrefix = _hostName + '/'; + _logger = logger; + } - public async Task SubscribeAsync(string channel, Action handler) + public async Task SubscribeAsync(string channel, Action handler) + { + if (_redis.Connection == null) { + await _redis.ConnectAsync(); if (_redis.Connection == null) { - await _redis.ConnectAsync(); - if (_redis.Connection == null) - { - _logger.LogError("Unable to subscribe to the channel '{ChannelName}'.", _channelPrefix + channel); - return; - } + _logger.LogError("Unable to subscribe to the channel '{ChannelName}'.", _channelPrefix + channel); + return; } + } + + try + { + var subscriber = _redis.Connection.GetSubscriber(); - try + await subscriber.SubscribeAsync(RedisChannel.Literal(_channelPrefix + channel), (redisChannel, redisValue) => { - var subscriber = _redis.Connection.GetSubscriber(); + var tokens = redisValue.ToString().Split('/').ToArray(); - await subscriber.SubscribeAsync(RedisChannel.Literal(_channelPrefix + channel), (redisChannel, redisValue) => + if (tokens.Length != 2 || tokens[0].Length == 0 || tokens[0].Equals(_hostName, StringComparison.OrdinalIgnoreCase)) { - var tokens = redisValue.ToString().Split('/').ToArray(); - - if (tokens.Length != 2 || tokens[0].Length == 0 || tokens[0].Equals(_hostName, StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - handler(channel, tokens[1]); - }); - } - catch (Exception e) - { - _logger.LogError(e, "Unable to subscribe to the channel '{ChannelName}'.", _channelPrefix + channel); - } + handler(channel, tokens[1]); + }); } + catch (Exception e) + { + _logger.LogError(e, "Unable to subscribe to the channel '{ChannelName}'.", _channelPrefix + channel); + } + } - public async Task PublishAsync(string channel, string message) + public async Task PublishAsync(string channel, string message) + { + if (_redis.Connection == null) { + await _redis.ConnectAsync(); if (_redis.Connection == null) { - await _redis.ConnectAsync(); - if (_redis.Connection == null) - { - _logger.LogError("Unable to publish to the channel '{ChannelName}'.", _channelPrefix + channel); - return; - } + _logger.LogError("Unable to publish to the channel '{ChannelName}'.", _channelPrefix + channel); + return; } + } - try - { - await _redis.Connection.GetSubscriber().PublishAsync(RedisChannel.Literal(_channelPrefix + channel), _messagePrefix + message); - } - catch (Exception e) - { - _logger.LogError(e, "Unable to publish to the channel '{ChannelName}'.", _channelPrefix + channel); - } + try + { + await _redis.Connection.GetSubscriber().PublishAsync(RedisChannel.Literal(_channelPrefix + channel), _messagePrefix + message); + } + catch (Exception e) + { + _logger.LogError(e, "Unable to publish to the channel '{ChannelName}'.", _channelPrefix + channel); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisLock.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisLock.cs index dc24da9ce9d..1e4eb204919 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisLock.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisLock.cs @@ -8,204 +8,203 @@ using OrchardCore.Locking.Distributed; using StackExchange.Redis; -namespace OrchardCore.Redis.Services +namespace OrchardCore.Redis.Services; + +/// +/// This component is a tenant singleton which allows to acquire distributed named locks for a given tenant. +/// +public class RedisLock : IDistributedLock { + private readonly IRedisService _redis; + private readonly ILogger _logger; + private readonly string _hostName; + private readonly string _prefix; + + public RedisLock(IRedisService redis, ShellSettings shellSettings, ILogger logger) + { + _redis = redis; + _hostName = Dns.GetHostName() + ':' + System.Environment.ProcessId; + _prefix = redis.InstancePrefix + shellSettings.Name + ':'; + _logger = logger; + } + /// - /// This component is a tenant singleton which allows to acquire distributed named locks for a given tenant. + /// Waits indefinitely until acquiring a named lock with a given expiration for the current tenant. + /// After 'expiration' the lock is auto released, a null value is equivalent to 'TimeSpan.MaxValue'. /// - public class RedisLock : IDistributedLock + public async Task AcquireLockAsync(string key, TimeSpan? expiration = null) { - private readonly IRedisService _redis; - private readonly ILogger _logger; - private readonly string _hostName; - private readonly string _prefix; + return (await TryAcquireLockAsync(key, TimeSpan.MaxValue, expiration)).locker; + } - public RedisLock(IRedisService redis, ShellSettings shellSettings, ILogger logger) + /// + /// Tries to acquire a named lock in a given timeout with a given expiration for the current tenant. + /// After 'expiration' the lock is auto released, a null value is equivalent to 'TimeSpan.MaxValue'. + /// + public async Task<(ILocker locker, bool locked)> TryAcquireLockAsync(string key, TimeSpan timeout, TimeSpan? expiration = null) + { + using (var cts = new CancellationTokenSource(timeout != TimeSpan.MaxValue ? timeout : Timeout.InfiniteTimeSpan)) { - _redis = redis; - _hostName = Dns.GetHostName() + ':' + System.Environment.ProcessId; - _prefix = redis.InstancePrefix + shellSettings.Name + ':'; - _logger = logger; - } + var retries = 0.0; - /// - /// Waits indefinitely until acquiring a named lock with a given expiration for the current tenant. - /// After 'expiration' the lock is auto released, a null value is equivalent to 'TimeSpan.MaxValue'. - /// - public async Task AcquireLockAsync(string key, TimeSpan? expiration = null) - { - return (await TryAcquireLockAsync(key, TimeSpan.MaxValue, expiration)).locker; - } - - /// - /// Tries to acquire a named lock in a given timeout with a given expiration for the current tenant. - /// After 'expiration' the lock is auto released, a null value is equivalent to 'TimeSpan.MaxValue'. - /// - public async Task<(ILocker locker, bool locked)> TryAcquireLockAsync(string key, TimeSpan timeout, TimeSpan? expiration = null) - { - using (var cts = new CancellationTokenSource(timeout != TimeSpan.MaxValue ? timeout : Timeout.InfiniteTimeSpan)) + while (!cts.IsCancellationRequested) { - var retries = 0.0; + var locked = await LockAsync(key, expiration ?? TimeSpan.MaxValue); - while (!cts.IsCancellationRequested) + if (locked) { - var locked = await LockAsync(key, expiration ?? TimeSpan.MaxValue); - - if (locked) - { - return (new Locker(this, key), locked); - } + return (new Locker(this, key), locked); + } - try - { - await Task.Delay(GetDelay(++retries), cts.Token); - } - catch (TaskCanceledException) + try + { + await Task.Delay(GetDelay(++retries), cts.Token); + } + catch (TaskCanceledException) + { + if (_logger.IsEnabled(LogLevel.Debug)) { - if (_logger.IsEnabled(LogLevel.Debug)) - { - _logger.LogDebug("Timeout elapsed before acquiring the named lock '{LockName}' after the given timeout of '{Timeout}'.", - _prefix + key, timeout.ToString()); - } + _logger.LogDebug("Timeout elapsed before acquiring the named lock '{LockName}' after the given timeout of '{Timeout}'.", + _prefix + key, timeout.ToString()); } } } - - return (null, false); } - public async Task IsLockAcquiredAsync(string key) + return (null, false); + } + + public async Task IsLockAcquiredAsync(string key) + { + if (_redis.Database == null) { + await _redis.ConnectAsync(); if (_redis.Database == null) { - await _redis.ConnectAsync(); - if (_redis.Database == null) - { - _logger.LogError("Fails to check whether the named lock '{LockName}' is already acquired.", _prefix + key); - return false; - } - } - - try - { - return (await _redis.Database.LockQueryAsync(_prefix + key)).HasValue; - } - catch (Exception e) - { - _logger.LogError(e, "Fails to check whether the named lock '{LockName}' is already acquired.", _prefix + key); + _logger.LogError("Fails to check whether the named lock '{LockName}' is already acquired.", _prefix + key); + return false; } + } - return false; + try + { + return (await _redis.Database.LockQueryAsync(_prefix + key)).HasValue; + } + catch (Exception e) + { + _logger.LogError(e, "Fails to check whether the named lock '{LockName}' is already acquired.", _prefix + key); } - private async Task LockAsync(string key, TimeSpan expiry) + return false; + } + + private async Task LockAsync(string key, TimeSpan expiry) + { + if (_redis.Database == null) { + await _redis.ConnectAsync(); if (_redis.Database == null) { - await _redis.ConnectAsync(); - if (_redis.Database == null) - { - _logger.LogError("Fails to acquire the named lock '{LockName}'.", _prefix + key); - return false; - } - } - - try - { - return await _redis.Database.LockTakeAsync(_prefix + key, _hostName, expiry); - } - catch (Exception e) - { - _logger.LogError(e, "Fails to acquire the named lock '{LockName}'.", _prefix + key); + _logger.LogError("Fails to acquire the named lock '{LockName}'.", _prefix + key); + return false; } + } - return false; + try + { + return await _redis.Database.LockTakeAsync(_prefix + key, _hostName, expiry); } + catch (Exception e) + { + _logger.LogError(e, "Fails to acquire the named lock '{LockName}'.", _prefix + key); + } + + return false; + } - private async ValueTask ReleaseAsync(string key) + private async ValueTask ReleaseAsync(string key) + { + try { - try - { - await _redis.Database.LockReleaseAsync(_prefix + key, _hostName); - } - catch (Exception e) - { - _logger.LogError(e, "Fails to release the named lock '{LockName}'.", _prefix + key); - } + await _redis.Database.LockReleaseAsync(_prefix + key, _hostName); } + catch (Exception e) + { + _logger.LogError(e, "Fails to release the named lock '{LockName}'.", _prefix + key); + } + } - private void Release(string key) + private void Release(string key) + { + try { - try - { - _redis.Database.LockRelease(_prefix + key, _hostName, CommandFlags.FireAndForget); - } - catch (Exception e) - { - _logger.LogError(e, "Fails to release the named lock '{LockName}'.", _prefix + key); - } + _redis.Database.LockRelease(_prefix + key, _hostName, CommandFlags.FireAndForget); } + catch (Exception e) + { + _logger.LogError(e, "Fails to release the named lock '{LockName}'.", _prefix + key); + } + } - private sealed class Locker : ILocker + private sealed class Locker : ILocker + { + private readonly RedisLock _lock; + private readonly string _key; + private bool _disposed; + + public Locker(RedisLock redislock, string key) { - private readonly RedisLock _lock; - private readonly string _key; - private bool _disposed; + _lock = redislock; + _key = key; + } - public Locker(RedisLock redislock, string key) + public ValueTask DisposeAsync() + { + if (_disposed) { - _lock = redislock; - _key = key; + return default; } - public ValueTask DisposeAsync() - { - if (_disposed) - { - return default; - } - - _disposed = true; + _disposed = true; - return _lock.ReleaseAsync(_key); - } + return _lock.ReleaseAsync(_key); + } - public void Dispose() + public void Dispose() + { + if (_disposed) { - if (_disposed) - { - return; - } + return; + } - _disposed = true; + _disposed = true; - _lock.Release(_key); - } + _lock.Release(_key); } + } - private static readonly double _baseDelay = 100; - private static readonly double _maxDelay = 10000; + private static readonly double _baseDelay = 100; + private static readonly double _maxDelay = 10000; - private static TimeSpan GetDelay(double retries) - { - var delay = _baseDelay - * (1.0 + ((Math.Pow(1.8, retries - 1.0) - 1.0) - * (0.6 + new Random().NextDouble() * 0.4))); - - return TimeSpan.FromMilliseconds(Math.Min(delay, _maxDelay)); - - // 2 examples with 10 retries - // -------------------------- - // 100 100 (start from base) - // 164 171 - // 256 312 - // 401 519 - // 754 766 - // 1327 1562 - // 2950 3257 - // 4596 4966 - // 7215 8667 - // 10000 10000 (max reached) - } + private static TimeSpan GetDelay(double retries) + { + var delay = _baseDelay + * (1.0 + ((Math.Pow(1.8, retries - 1.0) - 1.0) + * (0.6 + new Random().NextDouble() * 0.4))); + + return TimeSpan.FromMilliseconds(Math.Min(delay, _maxDelay)); + + // 2 examples with 10 retries + // -------------------------- + // 100 100 (start from base) + // 164 171 + // 256 312 + // 401 519 + // 754 766 + // 1327 1562 + // 2950 3257 + // 4596 4966 + // 7215 8667 + // 10000 10000 (max reached) } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisService.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisService.cs index 92b245bb15f..d9b64e024bc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisService.cs @@ -3,27 +3,26 @@ using OrchardCore.Modules; using StackExchange.Redis; -namespace OrchardCore.Redis.Services +namespace OrchardCore.Redis.Services; + +public class RedisService : ModularTenantEvents, IRedisService { - public class RedisService : ModularTenantEvents, IRedisService - { - private readonly IRedisDatabaseFactory _factory; - private readonly RedisOptions _options; + private readonly IRedisDatabaseFactory _factory; + private readonly RedisOptions _options; - public RedisService(IRedisDatabaseFactory factory, IOptions options) - { - _factory = factory; - _options = options.Value; - } + public RedisService(IRedisDatabaseFactory factory, IOptions options) + { + _factory = factory; + _options = options.Value; + } - public IConnectionMultiplexer Connection => Database?.Multiplexer; + public IConnectionMultiplexer Connection => Database?.Multiplexer; - public string InstancePrefix => _options.InstancePrefix; + public string InstancePrefix => _options.InstancePrefix; - public IDatabase Database { get; private set; } + public IDatabase Database { get; private set; } - public override Task ActivatingAsync() => ConnectAsync(); + public override Task ActivatingAsync() => ConnectAsync(); - public async Task ConnectAsync() => Database ??= await _factory.CreateAsync(_options); - } + public async Task ConnectAsync() => Database ??= await _factory.CreateAsync(_options); } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisTagCache.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisTagCache.cs index 95af43a1f5f..cfabca455d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisTagCache.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Services/RedisTagCache.cs @@ -7,114 +7,113 @@ using OrchardCore.Environment.Shell; using OrchardCore.Modules; -namespace OrchardCore.Redis.Services +namespace OrchardCore.Redis.Services; + +public class RedisTagCache : ITagCache { - public class RedisTagCache : ITagCache + private readonly IRedisService _redis; + private readonly string _prefix; + private readonly IEnumerable _tagRemovedEventHandlers; + private readonly ILogger _logger; + + public RedisTagCache( + IRedisService redis, + ShellSettings shellSettings, + IEnumerable tagRemovedEventHandlers, + ILogger logger) { - private readonly IRedisService _redis; - private readonly string _prefix; - private readonly IEnumerable _tagRemovedEventHandlers; - private readonly ILogger _logger; - - public RedisTagCache( - IRedisService redis, - ShellSettings shellSettings, - IEnumerable tagRemovedEventHandlers, - ILogger logger) - { - _redis = redis; - _prefix = redis.InstancePrefix + shellSettings.Name + ":Tag:"; - _tagRemovedEventHandlers = tagRemovedEventHandlers; - _logger = logger; - } + _redis = redis; + _prefix = redis.InstancePrefix + shellSettings.Name + ":Tag:"; + _tagRemovedEventHandlers = tagRemovedEventHandlers; + _logger = logger; + } - public async Task TagAsync(string key, params string[] tags) + public async Task TagAsync(string key, params string[] tags) + { + if (_redis.Database == null) { + await _redis.ConnectAsync(); if (_redis.Database == null) { - await _redis.ConnectAsync(); - if (_redis.Database == null) - { - _logger.LogError("Fails to add the '{KeyName}' to the {PrefixName} tags.", key, _prefix); - return; - } + _logger.LogError("Fails to add the '{KeyName}' to the {PrefixName} tags.", key, _prefix); + return; } + } - try - { - foreach (var tag in tags) - { - await _redis.Database.SetAddAsync(_prefix + tag, key); - } - } - catch (Exception e) + try + { + foreach (var tag in tags) { - _logger.LogError(e, "Fails to add the '{KeyName}' to the {PrefixName} tags.", key, _prefix); + await _redis.Database.SetAddAsync(_prefix + tag, key); } } + catch (Exception e) + { + _logger.LogError(e, "Fails to add the '{KeyName}' to the {PrefixName} tags.", key, _prefix); + } + } - public async Task> GetTaggedItemsAsync(string tag) + public async Task> GetTaggedItemsAsync(string tag) + { + if (_redis.Database == null) { + await _redis.ConnectAsync(); if (_redis.Database == null) { - await _redis.ConnectAsync(); - if (_redis.Database == null) - { - _logger.LogError("Fails to get '{TagName}' items.", _prefix + tag); - return []; - } + _logger.LogError("Fails to get '{TagName}' items.", _prefix + tag); + return []; } + } - try - { - var values = await _redis.Database.SetMembersAsync(_prefix + tag); - - if (values == null || values.Length == 0) - { - return []; - } + try + { + var values = await _redis.Database.SetMembersAsync(_prefix + tag); - return values.Select(v => (string)v).ToArray(); - } - catch (Exception e) + if (values == null || values.Length == 0) { - _logger.LogError(e, "Fails to get '{TagName}' items.", _prefix + tag); + return []; } - return []; + return values.Select(v => (string)v).ToArray(); + } + catch (Exception e) + { + _logger.LogError(e, "Fails to get '{TagName}' items.", _prefix + tag); } - public async Task RemoveTagAsync(string tag) + return []; + } + + public async Task RemoveTagAsync(string tag) + { + if (_redis.Database == null) { + await _redis.ConnectAsync(); if (_redis.Database == null) { - await _redis.ConnectAsync(); - if (_redis.Database == null) - { - _logger.LogError("Fails to remove the '{TagName}'.", _prefix + tag); - return; - } + _logger.LogError("Fails to remove the '{TagName}'.", _prefix + tag); + return; } + } - try - { - var values = await _redis.Database.SetMembersAsync(_prefix + tag); + try + { + var values = await _redis.Database.SetMembersAsync(_prefix + tag); - if (values == null || values.Length == 0) - { - return; - } + if (values == null || values.Length == 0) + { + return; + } - var set = values.Select(v => (string)v).ToArray(); + var set = values.Select(v => (string)v).ToArray(); - await _redis.Database.KeyDeleteAsync(_prefix + tag); + await _redis.Database.KeyDeleteAsync(_prefix + tag); - await _tagRemovedEventHandlers.InvokeAsync(x => x.TagRemovedAsync(tag, set), _logger); - } - catch (Exception e) - { - _logger.LogError(e, "Fails to remove the '{TagName}'.", _prefix + tag); - } + await _tagRemovedEventHandlers.InvokeAsync(x => x.TagRemovedAsync(tag, set), _logger); + } + catch (Exception e) + { + _logger.LogError(e, "Fails to remove the '{TagName}'.", _prefix + tag); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Redis/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Redis/Startup.cs index cd3604af529..516a7e846bd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Redis/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Redis/Startup.cs @@ -19,107 +19,106 @@ using OrchardCore.Redis.Services; using StackExchange.Redis; -namespace OrchardCore.Redis +namespace OrchardCore.Redis; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - private readonly string _tenant; - private readonly IShellConfiguration _configuration; - private readonly ILogger _logger; + private readonly string _tenant; + private readonly IShellConfiguration _configuration; + private readonly ILogger _logger; - public Startup(ShellSettings shellSettings, IShellConfiguration configuration, ILogger logger) - { - _tenant = shellSettings.Name; - _configuration = configuration; - _logger = logger; - } + public Startup(ShellSettings shellSettings, IShellConfiguration configuration, ILogger logger) + { + _tenant = shellSettings.Name; + _configuration = configuration; + _logger = logger; + } - public override void ConfigureServices(IServiceCollection services) + public override void ConfigureServices(IServiceCollection services) + { + try { - try - { - var section = _configuration.GetSection("OrchardCore_Redis"); + var section = _configuration.GetSection("OrchardCore_Redis"); - var configuration = section["Configuration"]; - var configurationOptions = ConfigurationOptions.Parse(configuration); - var instancePrefix = section["InstancePrefix"]; + var configuration = section["Configuration"]; + var configurationOptions = ConfigurationOptions.Parse(configuration); + var instancePrefix = section["InstancePrefix"]; - if (section.GetValue("DisableCertificateVerification", false)) - { - configurationOptions.CertificateValidation += IgnoreCertificateErrors; - } - - services.Configure(options => - { - options.Configuration = configuration; - options.ConfigurationOptions = configurationOptions; - options.InstancePrefix = instancePrefix; - }); - } - catch (Exception e) + if (section.GetValue("DisableCertificateVerification", false)) { - _logger.LogError(e, "'Redis' features are not active on tenant '{TenantName}' as the 'Configuration' string is missing or invalid.", _tenant); - return; + configurationOptions.CertificateValidation += IgnoreCertificateErrors; } - services.AddSingleton(); - services.AddSingleton(sp => sp.GetRequiredService()); - services.AddSingleton(); + services.Configure(options => + { + options.Configuration = configuration; + options.ConfigurationOptions = configurationOptions; + options.InstancePrefix = instancePrefix; + }); + } + catch (Exception e) + { + _logger.LogError(e, "'Redis' features are not active on tenant '{TenantName}' as the 'Configuration' string is missing or invalid.", _tenant); + return; } - // Callback for accepting any certificate as long as it exists, while ignoring other SSL policy errors. - // This allows the use of self-signed certificates on the Redis server. - private static bool IgnoreCertificateErrors(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - => (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) == 0; + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + services.AddSingleton(); } - [Feature("OrchardCore.Redis.Cache")] - public sealed class RedisCacheStartup : StartupBase + // Callback for accepting any certificate as long as it exists, while ignoring other SSL policy errors. + // This allows the use of self-signed certificates on the Redis server. + private static bool IgnoreCertificateErrors(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + => (sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) == 0; +} + +[Feature("OrchardCore.Redis.Cache")] +public sealed class RedisCacheStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + if (services.Any(d => d.ServiceType == typeof(IRedisService))) { - if (services.Any(d => d.ServiceType == typeof(IRedisService))) - { - services.AddSingleton(); - services.AddTransient, RedisCacheOptionsSetup>(); - services.AddScoped(); - } + services.AddSingleton(); + services.AddTransient, RedisCacheOptionsSetup>(); + services.AddScoped(); } } +} - [Feature("OrchardCore.Redis.Bus")] - public sealed class RedisBusStartup : StartupBase +[Feature("OrchardCore.Redis.Bus")] +public sealed class RedisBusStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + if (services.Any(d => d.ServiceType == typeof(IRedisService))) { - if (services.Any(d => d.ServiceType == typeof(IRedisService))) - { - services.AddSingleton(); - } + services.AddSingleton(); } } +} - [Feature("OrchardCore.Redis.Lock")] - public sealed class RedisLockStartup : StartupBase +[Feature("OrchardCore.Redis.Lock")] +public sealed class RedisLockStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + if (services.Any(d => d.ServiceType == typeof(IRedisService))) { - if (services.Any(d => d.ServiceType == typeof(IRedisService))) - { - services.AddSingleton(); - } + services.AddSingleton(); } } +} - [Feature("OrchardCore.Redis.DataProtection")] - public sealed class RedisDataProtectionStartup : StartupBase +[Feature("OrchardCore.Redis.DataProtection")] +public sealed class RedisDataProtectionStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + if (services.Any(d => d.ServiceType == typeof(IRedisService))) { - if (services.Any(d => d.ServiceType == typeof(IRedisService))) - { - services.AddTransient, RedisKeyManagementOptionsSetup>(); - } + services.AddTransient, RedisKeyManagementOptionsSetup>(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/LinkTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/LinkTag.cs index 665404d82b6..268a3301f17 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/LinkTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/LinkTag.cs @@ -8,81 +8,80 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class LinkTag { - public class LinkTag + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter _1, TextEncoder _2, TemplateContext context) { - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter _1, TextEncoder _2, TemplateContext context) - { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); - string src = null; - string rel = null; - string condition = null; - string title = null; - string type = null; - bool? appendVersion = null; + string src = null; + string rel = null; + string condition = null; + string title = null; + string type = null; + bool? appendVersion = null; - Dictionary customAttributes = null; + Dictionary customAttributes = null; - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "rel": rel = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "title": title = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "type": type = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - } + case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "rel": rel = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "title": title = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "type": type = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; } + } - var linkEntry = new LinkEntry(); + var linkEntry = new LinkEntry(); - if (!string.IsNullOrEmpty(src)) - { - linkEntry.Href = src; - } + if (!string.IsNullOrEmpty(src)) + { + linkEntry.Href = src; + } - if (!string.IsNullOrEmpty(rel)) - { - linkEntry.Rel = rel; - } + if (!string.IsNullOrEmpty(rel)) + { + linkEntry.Rel = rel; + } - if (!string.IsNullOrEmpty(condition)) - { - linkEntry.Condition = condition; - } + if (!string.IsNullOrEmpty(condition)) + { + linkEntry.Condition = condition; + } - if (!string.IsNullOrEmpty(title)) - { - linkEntry.Title = title; - } + if (!string.IsNullOrEmpty(title)) + { + linkEntry.Title = title; + } - if (!string.IsNullOrEmpty(type)) - { - linkEntry.Type = type; - } + if (!string.IsNullOrEmpty(type)) + { + linkEntry.Type = type; + } - if (appendVersion.HasValue) - { - linkEntry.AppendVersion = appendVersion.Value; - } + if (appendVersion.HasValue) + { + linkEntry.AppendVersion = appendVersion.Value; + } - if (customAttributes != null) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - foreach (var attribute in customAttributes) - { - linkEntry.SetAttribute(attribute.Key, attribute.Value); - } + linkEntry.SetAttribute(attribute.Key, attribute.Value); } + } - resourceManager.RegisterLink(linkEntry); + resourceManager.RegisterLink(linkEntry); - return Completion.Normal; - } + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/MetaTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/MetaTag.cs index b2def0e9ecb..634d16c3b3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/MetaTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/MetaTag.cs @@ -8,51 +8,50 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class MetaTag { - public class MetaTag + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter _1, TextEncoder _2, TemplateContext context) { - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter _1, TextEncoder _2, TemplateContext context) - { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); - string name = null; - string property = null; - string content = null; - string httpEquiv = null; - string charset = null; - string separator = null; + string name = null; + string property = null; + string content = null; + string httpEquiv = null; + string charset = null; + string separator = null; - Dictionary customAttributes = null; + Dictionary customAttributes = null; - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "property": property = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "content": content = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "http_equiv": httpEquiv = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "charset": charset = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "separator": separator = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - } + case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "property": property = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "content": content = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "http_equiv": httpEquiv = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "charset": charset = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "separator": separator = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; } + } - var metaEntry = new MetaEntry(name, property, content, httpEquiv, charset); + var metaEntry = new MetaEntry(name, property, content, httpEquiv, charset); - if (customAttributes != null) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - foreach (var attribute in customAttributes) - { - metaEntry.SetAttribute(attribute.Key, attribute.Value); - } + metaEntry.SetAttribute(attribute.Key, attribute.Value); } + } - resourceManager.AppendMeta(metaEntry, separator ?? ", "); + resourceManager.AppendMeta(metaEntry, separator ?? ", "); - return Completion.Normal; - } + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs index 88770c2ed9c..d23676a7706 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs @@ -9,38 +9,37 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class ResourcesTag { - public class ResourcesTag + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) { - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) - { - var services = ((LiquidTemplateContext)context).Services; - var processors = services.GetRequiredService>(); + var services = ((LiquidTemplateContext)context).Services; + var processors = services.GetRequiredService>(); - var processorContext = new ResourcesTagHelperProcessorContext(ResourceTagType.Footer, writer); + var processorContext = new ResourcesTagHelperProcessorContext(ResourceTagType.Footer, writer); - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "type": - var typeString = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); - if (Enum.TryParse(typeString, out var type)) - { - processorContext = processorContext with { Type = type }; - } + case "type": + var typeString = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); + if (Enum.TryParse(typeString, out var type)) + { + processorContext = processorContext with { Type = type }; + } - break; - } - } - - foreach (var processor in processors) - { - await processor.ProcessAsync(processorContext); + break; } + } - return Completion.Normal; + foreach (var processor in processors) + { + await processor.ProcessAsync(processorContext); } + + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptBlock.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptBlock.cs index 1b1b588bdad..6f23b765cfc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptBlock.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptBlock.cs @@ -11,124 +11,88 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class ScriptBlock { - public class ScriptBlock - { - private static readonly char[] _separators = [',', ' ']; + private static readonly char[] _separators = [',', ' ']; - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, TemplateContext context) - { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, TemplateContext context) + { + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); - string name = null; - bool? useCdn = null; - string condition = null; - string culture = null; - bool? debug = null; - string dependsOn = null; - string version = null; - var at = ResourceLocation.Unspecified; + string name = null; + bool? useCdn = null; + string condition = null; + string culture = null; + bool? debug = null; + string dependsOn = null; + string version = null; + var at = ResourceLocation.Unspecified; - Dictionary customAttributes = null; + Dictionary customAttributes = null; - foreach (var argument in argumentsList) + foreach (var argument in argumentsList) + { + switch (argument.Name) { - switch (argument.Name) - { - case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - } + case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; } + } - if (!string.IsNullOrEmpty(name)) - { - // Resource required - - var setting = resourceManager.RegisterResource("script", name); - - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } - - if (useCdn != null) - { - setting.UseCdn(useCdn.Value); - } - - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } - - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } - - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } - - if (!string.IsNullOrEmpty(version)) - { - setting.UseVersion(version); - } + if (!string.IsNullOrEmpty(name)) + { + // Resource required - // This allows additions to the pre registered scripts dependencies. - if (!string.IsNullOrEmpty(dependsOn)) - { - setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + var setting = resourceManager.RegisterResource("script", name); - // Allow Inline to work with both named scripts, and named inline scripts. - if (at != ResourceLocation.Unspecified) - { - var content = ""; + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } - if (statements != null && statements.Count > 0) - { - using var sw = new ZStringWriter(); - var completion = await statements.RenderStatementsAsync(sw, encoder, context); + if (useCdn != null) + { + setting.UseCdn(useCdn.Value); + } - if (completion != Completion.Normal) - { - return completion; - } + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - content = sw.ToString(); - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); + } - // Named inline declaration. - if (!string.IsNullOrWhiteSpace(content)) - { - // Inline content definition - resourceManager.InlineManifest.DefineScript(name).SetInnerContent(content); - } + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - if (at == ResourceLocation.Inline) - { - resourceManager.RenderLocalScript(setting, writer); - } - } - else - { - resourceManager.RenderLocalScript(setting, writer); - } + if (!string.IsNullOrEmpty(version)) + { + setting.UseVersion(version); } - else + + // This allows additions to the pre registered scripts dependencies. + if (!string.IsNullOrEmpty(dependsOn)) { - // Custom script content + setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } + // Allow Inline to work with both named scripts, and named inline scripts. + if (at != ResourceLocation.Unspecified) + { var content = ""; if (statements != null && statements.Count > 0) @@ -144,33 +108,68 @@ public static async ValueTask WriteToAsync(IReadOnlyList 0) + { + using var sw = new ZStringWriter(); + var completion = await statements.RenderStatementsAsync(sw, encoder, context); + + if (completion != Completion.Normal) { - builder.WriteTo(writer, (HtmlEncoder)encoder); + return completion; } - else + + content = sw.ToString(); + } + + var builder = new TagBuilder("script"); + builder.InnerHtml.AppendHtml(content); + builder.TagRenderMode = TagRenderMode.Normal; + + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - resourceManager.RegisterFootScript(builder); + builder.Attributes.Add(attribute.Key, attribute.Value); } } - return Completion.Normal; + if (at == ResourceLocation.Head) + { + resourceManager.RegisterHeadScript(builder); + } + else if (at == ResourceLocation.Inline) + { + builder.WriteTo(writer, (HtmlEncoder)encoder); + } + else + { + resourceManager.RegisterFootScript(builder); + } } + + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptTag.cs index 118e074e4dd..cfe65a5a460 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ScriptTag.cs @@ -9,284 +9,283 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class ScriptTag { - public class ScriptTag - { - private static readonly char[] _separators = [',', ' ']; + private static readonly char[] _separators = [',', ' ']; - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) + { + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); + + string name = null; + string src = null; + bool? appendVersion = null; + string cdnSrc = null; + string debugSrc = null; + string debugCdnSrc = null; + bool? useCdn = null; + string condition = null; + string culture = null; + bool? debug = null; + string dependsOn = null; + string version = null; + var at = ResourceLocation.Unspecified; + + Dictionary customAttributes = null; + + foreach (var argument in argumentsList) { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); - - string name = null; - string src = null; - bool? appendVersion = null; - string cdnSrc = null; - string debugSrc = null; - string debugCdnSrc = null; - bool? useCdn = null; - string condition = null; - string culture = null; - bool? debug = null; - string dependsOn = null; - string version = null; - var at = ResourceLocation.Unspecified; - - Dictionary customAttributes = null; - - foreach (var argument in argumentsList) + switch (argument.Name) { - switch (argument.Name) - { - case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "cdn_src": cdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug_src": debugSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug_cdn_src": debugCdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "use_cdn": useCdn = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - } + case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "append_version": appendVersion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "cdn_src": cdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug_src": debugSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug_cdn_src": debugCdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "use_cdn": useCdn = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; } + } - if (string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) - { - // {% script src:"~/TheBlogTheme/js/clean-blog.min.js" %} - RequireSettings setting; - - if (string.IsNullOrEmpty(dependsOn)) - { - // Include custom script url - setting = resourceManager.RegisterUrl("script", src, debugSrc); - } - else - { - // Anonymous declaration with dependencies, then display - - // Using the source as the name to prevent duplicate references to the same file - var s = src.ToLowerInvariant(); - - var definition = resourceManager.InlineManifest.DefineScript(s); - definition.SetUrl(src, debugSrc); - - if (!string.IsNullOrEmpty(version)) - { - definition.SetVersion(version); - } - - if (!string.IsNullOrEmpty(cdnSrc)) - { - definition.SetCdn(cdnSrc, debugCdnSrc); - } - - if (!string.IsNullOrEmpty(culture)) - { - definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } - - definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - - if (appendVersion.HasValue) - { - definition.ShouldAppendVersion(appendVersion); - } + if (string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + { + // {% script src:"~/TheBlogTheme/js/clean-blog.min.js" %} + RequireSettings setting; - if (!string.IsNullOrEmpty(version)) - { - definition.SetVersion(version); - } + if (string.IsNullOrEmpty(dependsOn)) + { + // Include custom script url + setting = resourceManager.RegisterUrl("script", src, debugSrc); + } + else + { + // Anonymous declaration with dependencies, then display - setting = resourceManager.RegisterResource("script", s); - } + // Using the source as the name to prevent duplicate references to the same file + var s = src.ToLowerInvariant(); - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } + var definition = resourceManager.InlineManifest.DefineScript(s); + definition.SetUrl(src, debugSrc); - if (!string.IsNullOrEmpty(condition)) + if (!string.IsNullOrEmpty(version)) { - setting.UseCondition(condition); + definition.SetVersion(version); } - if (debug != null) + if (!string.IsNullOrEmpty(cdnSrc)) { - setting.UseDebugMode(debug.Value); + definition.SetCdn(cdnSrc, debugCdnSrc); } if (!string.IsNullOrEmpty(culture)) { - setting.UseCulture(culture); + definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); } + definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + if (appendVersion.HasValue) { - setting.ShouldAppendVersion(appendVersion); + definition.ShouldAppendVersion(appendVersion); } - if (customAttributes != null) + if (!string.IsNullOrEmpty(version)) { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } + definition.SetVersion(version); } - if (at == ResourceLocation.Unspecified || at == ResourceLocation.Inline) - { - resourceManager.RenderLocalScript(setting, writer); - } + setting = resourceManager.RegisterResource("script", s); } - else if (!string.IsNullOrEmpty(name) && string.IsNullOrEmpty(src)) + + if (at != ResourceLocation.Unspecified) { - // Resource required - // {% script name:"bootstrap" %} + setting.AtLocation(at); + } - var setting = resourceManager.RegisterResource("script", name); + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); + } - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - if (useCdn != null) - { - setting.UseCdn(useCdn.Value); - } + if (appendVersion.HasValue) + { + setting.ShouldAppendVersion(appendVersion); + } - if (!string.IsNullOrEmpty(condition)) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - setting.UseCondition(condition); + setting.SetAttribute(attribute.Key, attribute.Value); } + } - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } + if (at == ResourceLocation.Unspecified || at == ResourceLocation.Inline) + { + resourceManager.RenderLocalScript(setting, writer); + } + } + else if (!string.IsNullOrEmpty(name) && string.IsNullOrEmpty(src)) + { + // Resource required + // {% script name:"bootstrap" %} - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } + var setting = resourceManager.RegisterResource("script", name); - if (appendVersion.HasValue) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - setting.ShouldAppendVersion(appendVersion); + setting.SetAttribute(attribute.Key, attribute.Value); } + } - if (!string.IsNullOrEmpty(version)) - { - setting.UseVersion(version); - } + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } - // This allows additions to the pre registered scripts dependencies. - if (!string.IsNullOrEmpty(dependsOn)) - { - setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + if (useCdn != null) + { + setting.UseCdn(useCdn.Value); + } - if (at == ResourceLocation.Unspecified || at == ResourceLocation.Inline) - { - resourceManager.RenderLocalScript(setting, writer); - } + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); } - else if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + + if (debug != null) { - // Inline declaration + setting.UseDebugMode(debug.Value); + } - var definition = resourceManager.InlineManifest.DefineScript(name); - definition.SetUrl(src, debugSrc); + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - if (!string.IsNullOrEmpty(version)) - { - definition.SetVersion(version); - } + if (appendVersion.HasValue) + { + setting.ShouldAppendVersion(appendVersion); + } - if (!string.IsNullOrEmpty(cdnSrc)) - { - definition.SetCdn(cdnSrc, debugCdnSrc); - } + if (!string.IsNullOrEmpty(version)) + { + setting.UseVersion(version); + } - if (!string.IsNullOrEmpty(culture)) + // This allows additions to the pre registered scripts dependencies. + if (!string.IsNullOrEmpty(dependsOn)) + { + setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } + + if (at == ResourceLocation.Unspecified || at == ResourceLocation.Inline) + { + resourceManager.RenderLocalScript(setting, writer); + } + } + else if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + { + // Inline declaration + + var definition = resourceManager.InlineManifest.DefineScript(name); + definition.SetUrl(src, debugSrc); + + if (!string.IsNullOrEmpty(version)) + { + definition.SetVersion(version); + } + + if (!string.IsNullOrEmpty(cdnSrc)) + { + definition.SetCdn(cdnSrc, debugCdnSrc); + } + + if (!string.IsNullOrEmpty(culture)) + { + definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } + + if (!string.IsNullOrEmpty(dependsOn)) + { + definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } + + if (appendVersion.HasValue) + { + definition.ShouldAppendVersion(appendVersion); + } + + if (!string.IsNullOrEmpty(version)) + { + definition.SetVersion(version); + } + + // If At is specified then we also render it + if (at != ResourceLocation.Unspecified) + { + var setting = resourceManager.RegisterResource("script", name); + + setting.AtLocation(at); + + if (useCdn != null) { - definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + setting.UseCdn(useCdn.Value); } - if (!string.IsNullOrEmpty(dependsOn)) + if (!string.IsNullOrEmpty(condition)) { - definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + setting.UseCondition(condition); } - if (appendVersion.HasValue) + if (debug != null) { - definition.ShouldAppendVersion(appendVersion); + setting.UseDebugMode(debug.Value); } - if (!string.IsNullOrEmpty(version)) + if (!string.IsNullOrEmpty(culture)) { - definition.SetVersion(version); + setting.UseCulture(culture); } - // If At is specified then we also render it - if (at != ResourceLocation.Unspecified) + if (customAttributes != null) { - var setting = resourceManager.RegisterResource("script", name); - - setting.AtLocation(at); - - if (useCdn != null) - { - setting.UseCdn(useCdn.Value); - } - - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } - - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } - - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } - - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } - } - - if (at == ResourceLocation.Inline) + foreach (var attribute in customAttributes) { - resourceManager.RenderLocalScript(setting, writer); + setting.SetAttribute(attribute.Key, attribute.Value); } + } + if (at == ResourceLocation.Inline) + { + resourceManager.RenderLocalScript(setting, writer); } - } - return Completion.Normal; + } } + + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleBlock.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleBlock.cs index 8f011159dfc..bfa93afff62 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleBlock.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleBlock.cs @@ -11,165 +11,164 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class StyleBlock { - public class StyleBlock + private static readonly char[] _separators = [',', ' ']; + + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, TemplateContext context) { - private static readonly char[] _separators = [',', ' ']; + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); + + string name = null; + string condition = null; + string culture = null; + bool? debug = null; + string dependsOn = null; + string version = null; + var at = ResourceLocation.Unspecified; - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, IReadOnlyList statements, TextWriter writer, TextEncoder encoder, TemplateContext context) + Dictionary customAttributes = null; + + foreach (var argument in argumentsList) { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); + switch (argument.Name) + { + case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + } + } - string name = null; - string condition = null; - string culture = null; - bool? debug = null; - string dependsOn = null; - string version = null; - var at = ResourceLocation.Unspecified; + if (!string.IsNullOrEmpty(name)) + { + // Resource required - Dictionary customAttributes = null; + var setting = resourceManager.RegisterResource("stylesheet", name); - foreach (var argument in argumentsList) + if (customAttributes != null) { - switch (argument.Name) + foreach (var attribute in customAttributes) { - case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + setting.SetAttribute(attribute.Key, attribute.Value); } } - if (!string.IsNullOrEmpty(name)) + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } + else { - // Resource required + setting.AtLocation(ResourceLocation.Head); + } - var setting = resourceManager.RegisterResource("stylesheet", name); + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); + } - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } - else - { - setting.AtLocation(ResourceLocation.Head); - } + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } + if (!string.IsNullOrEmpty(version)) + { + setting.UseVersion(version); + } - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } + // This allows additions to the pre registered style dependencies. + if (!string.IsNullOrEmpty(dependsOn)) + { + setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } + var content = ""; - if (!string.IsNullOrEmpty(version)) - { - setting.UseVersion(version); - } + if (statements != null && statements.Count > 0) + { + using var sw = new ZStringWriter(); + var completion = await statements.RenderStatementsAsync(sw, encoder, context); - // This allows additions to the pre registered style dependencies. - if (!string.IsNullOrEmpty(dependsOn)) + if (completion != Completion.Normal) { - setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + return completion; } - var content = ""; - - if (statements != null && statements.Count > 0) - { - using var sw = new ZStringWriter(); - var completion = await statements.RenderStatementsAsync(sw, encoder, context); + content = sw.ToString(); + } - if (completion != Completion.Normal) - { - return completion; - } + if (!string.IsNullOrWhiteSpace(content)) + { + // Inline named style definition + resourceManager.InlineManifest.DefineStyle(name).SetInnerContent(content); + } - content = sw.ToString(); - } + if (at == ResourceLocation.Inline) + { + resourceManager.RenderLocalStyle(setting, writer); + } + } + else + { + // Custom style content - if (!string.IsNullOrWhiteSpace(content)) - { - // Inline named style definition - resourceManager.InlineManifest.DefineStyle(name).SetInnerContent(content); - } + var content = ""; - if (at == ResourceLocation.Inline) - { - resourceManager.RenderLocalStyle(setting, writer); - } - } - else + if (statements != null && statements.Count > 0) { - // Custom style content - - var content = ""; + using var sw = new ZStringWriter(); + var completion = await statements.RenderStatementsAsync(sw, encoder, context); - if (statements != null && statements.Count > 0) + if (completion != Completion.Normal) { - using var sw = new ZStringWriter(); - var completion = await statements.RenderStatementsAsync(sw, encoder, context); - - if (completion != Completion.Normal) - { - return completion; - } - - content = sw.ToString(); + return completion; } - var builder = new TagBuilder("style"); - builder.InnerHtml.AppendHtml(content); - builder.TagRenderMode = TagRenderMode.Normal; + content = sw.ToString(); + } - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - builder.Attributes.Add(attribute.Key, attribute.Value); - } - } + var builder = new TagBuilder("style"); + builder.InnerHtml.AppendHtml(content); + builder.TagRenderMode = TagRenderMode.Normal; - // If no type was specified, define a default one - if (!builder.Attributes.ContainsKey("type")) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - builder.Attributes.Add("type", "text/css"); + builder.Attributes.Add(attribute.Key, attribute.Value); } + } - if (at == ResourceLocation.Inline) - { - builder.WriteTo(writer, (HtmlEncoder)encoder); - } - else - { - resourceManager.RegisterStyle(builder); - } + // If no type was specified, define a default one + if (!builder.Attributes.ContainsKey("type")) + { + builder.Attributes.Add("type", "text/css"); } - return Completion.Normal; + if (at == ResourceLocation.Inline) + { + builder.WriteTo(writer, (HtmlEncoder)encoder); + } + else + { + resourceManager.RegisterStyle(builder); + } } + + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleTag.cs index b611a8b93b8..a8359e1cf26 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/StyleTag.cs @@ -9,244 +9,243 @@ using OrchardCore.Liquid; using OrchardCore.ResourceManagement; -namespace OrchardCore.Resources.Liquid +namespace OrchardCore.Resources.Liquid; + +public class StyleTag { - public class StyleTag - { - private static readonly char[] _separators = [',', ' ']; + private static readonly char[] _separators = [',', ' ']; - public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) + public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) + { + var services = ((LiquidTemplateContext)context).Services; + var resourceManager = services.GetRequiredService(); + + string name = null; + string src = null; + bool? appendversion = null; + string cdnSrc = null; + string debugSrc = null; + string debugCdnSrc = null; + bool? useCdn = null; + string condition = null; + string culture = null; + bool? debug = null; + string dependsOn = null; + string version = null; + var at = ResourceLocation.Unspecified; + + Dictionary customAttributes = null; + + foreach (var argument in argumentsList) { - var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); - - string name = null; - string src = null; - bool? appendversion = null; - string cdnSrc = null; - string debugSrc = null; - string debugCdnSrc = null; - bool? useCdn = null; - string condition = null; - string culture = null; - bool? debug = null; - string dependsOn = null; - string version = null; - var at = ResourceLocation.Unspecified; - - Dictionary customAttributes = null; - - foreach (var argument in argumentsList) - { - switch (argument.Name) - { - case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "append_version": appendversion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "cdn_src": cdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug_src": debugSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug_cdn_src": debugCdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "use_cdn": useCdn = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; - case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; - default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; - } - } - - if (string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + switch (argument.Name) { - // Include custom style - var setting = resourceManager.RegisterUrl("stylesheet", src, debugSrc); - - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } - } - - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } - else - { - setting.AtLocation(ResourceLocation.Head); - } + case "name": name = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "src": src = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "append_version": appendversion = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "cdn_src": cdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug_src": debugSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug_cdn_src": debugCdnSrc = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "use_cdn": useCdn = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "condition": condition = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "culture": culture = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "debug": debug = (await argument.Expression.EvaluateAsync(context)).ToBooleanValue(); break; + case "depends_on": dependsOn = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "version": version = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + case "at": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), ignoreCase: true, out at); break; + default: (customAttributes ??= [])[argument.Name] = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); break; + } + } - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } + if (string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + { + // Include custom style + var setting = resourceManager.RegisterUrl("stylesheet", src, debugSrc); - if (appendversion.HasValue) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - setting.ShouldAppendVersion(appendversion); + setting.SetAttribute(attribute.Key, attribute.Value); } + } - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } + else + { + setting.AtLocation(ResourceLocation.Head); + } - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - if (!string.IsNullOrEmpty(dependsOn)) - { - setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + if (appendversion.HasValue) + { + setting.ShouldAppendVersion(appendversion); + } - if (at == ResourceLocation.Inline) - { - resourceManager.RenderLocalStyle(setting, writer); - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); } - else if (!string.IsNullOrEmpty(name) && string.IsNullOrEmpty(src)) + + if (!string.IsNullOrEmpty(culture)) { - // Resource required + setting.UseCulture(culture); + } - var setting = resourceManager.RegisterResource("stylesheet", name); + if (!string.IsNullOrEmpty(dependsOn)) + { + setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - setting.SetAttribute(attribute.Key, attribute.Value); - } - } + if (at == ResourceLocation.Inline) + { + resourceManager.RenderLocalStyle(setting, writer); + } + } + else if (!string.IsNullOrEmpty(name) && string.IsNullOrEmpty(src)) + { + // Resource required - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } - else - { - setting.AtLocation(ResourceLocation.Head); - } + var setting = resourceManager.RegisterResource("stylesheet", name); - if (useCdn != null) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - setting.UseCdn(useCdn.Value); + setting.SetAttribute(attribute.Key, attribute.Value); } + } - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } + else + { + setting.AtLocation(ResourceLocation.Head); + } - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } + if (useCdn != null) + { + setting.UseCdn(useCdn.Value); + } - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - if (appendversion.HasValue) - { - setting.ShouldAppendVersion(appendversion); - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); + } - if (!string.IsNullOrEmpty(version)) - { - setting.UseVersion(version); - } + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - // This allows additions to the pre registered style dependencies. - if (!string.IsNullOrEmpty(dependsOn)) - { - setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + if (appendversion.HasValue) + { + setting.ShouldAppendVersion(appendversion); + } - if (at == ResourceLocation.Inline) - { - resourceManager.RenderLocalStyle(setting, writer); - } + if (!string.IsNullOrEmpty(version)) + { + setting.UseVersion(version); } - else if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + + // This allows additions to the pre registered style dependencies. + if (!string.IsNullOrEmpty(dependsOn)) { - // Inline declaration + setting.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } - var definition = resourceManager.InlineManifest.DefineStyle(name); - definition.SetUrl(src, debugSrc); + if (at == ResourceLocation.Inline) + { + resourceManager.RenderLocalStyle(setting, writer); + } + } + else if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(src)) + { + // Inline declaration - if (customAttributes != null) - { - foreach (var attribute in customAttributes) - { - definition.SetAttribute(attribute.Key, attribute.Value); - } - } + var definition = resourceManager.InlineManifest.DefineStyle(name); + definition.SetUrl(src, debugSrc); - if (!string.IsNullOrEmpty(version)) + if (customAttributes != null) + { + foreach (var attribute in customAttributes) { - definition.SetVersion(version); + definition.SetAttribute(attribute.Key, attribute.Value); } + } - if (!string.IsNullOrEmpty(cdnSrc)) - { - definition.SetCdn(cdnSrc, debugCdnSrc); - } + if (!string.IsNullOrEmpty(version)) + { + definition.SetVersion(version); + } - if (!string.IsNullOrEmpty(culture)) - { - definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + if (!string.IsNullOrEmpty(cdnSrc)) + { + definition.SetCdn(cdnSrc, debugCdnSrc); + } - if (!string.IsNullOrEmpty(dependsOn)) - { - definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); - } + if (!string.IsNullOrEmpty(culture)) + { + definition.SetCultures(culture.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } - // Also include the style. - var setting = resourceManager.RegisterResource("stylesheet", name); + if (!string.IsNullOrEmpty(dependsOn)) + { + definition.SetDependencies(dependsOn.Split(_separators, StringSplitOptions.RemoveEmptyEntries)); + } - if (useCdn != null) - { - setting.UseCdn(useCdn.Value); - } + // Also include the style. + var setting = resourceManager.RegisterResource("stylesheet", name); - if (!string.IsNullOrEmpty(condition)) - { - setting.UseCondition(condition); - } + if (useCdn != null) + { + setting.UseCdn(useCdn.Value); + } - if (debug != null) - { - setting.UseDebugMode(debug.Value); - } + if (!string.IsNullOrEmpty(condition)) + { + setting.UseCondition(condition); + } - if (!string.IsNullOrEmpty(culture)) - { - setting.UseCulture(culture); - } + if (debug != null) + { + setting.UseDebugMode(debug.Value); + } - if (at != ResourceLocation.Unspecified) - { - setting.AtLocation(at); - } - else - { - setting.AtLocation(ResourceLocation.Head); - } + if (!string.IsNullOrEmpty(culture)) + { + setting.UseCulture(culture); + } - if (at == ResourceLocation.Inline) - { - resourceManager.RenderLocalStyle(setting, writer); - } + if (at != ResourceLocation.Unspecified) + { + setting.AtLocation(at); + } + else + { + setting.AtLocation(ResourceLocation.Head); } - return Completion.Normal; + if (at == ResourceLocation.Inline) + { + resourceManager.RenderLocalStyle(setting, writer); + } } + + return Completion.Normal; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Resources/ResourceManagementOptionsConfiguration.cs index 6b94502fa3e..3a56ad057dc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/ResourceManagementOptionsConfiguration.cs @@ -4,539 +4,538 @@ using OrchardCore.ResourceManagement; using OrchardCore.Settings; -namespace OrchardCore.Resources +namespace OrchardCore.Resources; + +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions + private readonly ResourceOptions _resourceOptions; + private readonly IHostEnvironment _env; + private readonly PathString _pathBase; + // Versions + private const string CodeMirrorVersion = "5.65.7"; + private const string MonacoEditorVersion = "0.46.0"; + // URLs + private const string CloudflareUrl = "https://cdnjs.cloudflare.com/ajax/libs/"; + private const string CodeMirrorUrl = CloudflareUrl + "codemirror/" + CodeMirrorVersion + "/"; + + public ResourceManagementOptionsConfiguration( + IOptions resourceOptions, + IHostEnvironment env, + IHttpContextAccessor httpContextAccessor) { - private readonly ResourceOptions _resourceOptions; - private readonly IHostEnvironment _env; - private readonly PathString _pathBase; - // Versions - private const string CodeMirrorVersion = "5.65.7"; - private const string MonacoEditorVersion = "0.46.0"; - // URLs - private const string CloudflareUrl = "https://cdnjs.cloudflare.com/ajax/libs/"; - private const string CodeMirrorUrl = CloudflareUrl + "codemirror/" + CodeMirrorVersion + "/"; - - public ResourceManagementOptionsConfiguration( - IOptions resourceOptions, - IHostEnvironment env, - IHttpContextAccessor httpContextAccessor) - { - _resourceOptions = resourceOptions.Value; - _env = env; - _pathBase = httpContextAccessor.HttpContext.Request.PathBase; - } + _resourceOptions = resourceOptions.Value; + _env = env; + _pathBase = httpContextAccessor.HttpContext.Request.PathBase; + } - ResourceManifest BuildManifest() - { - var manifest = new ResourceManifest(); - - manifest - .DefineScript("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery.min.js", "~/OrchardCore.Resources/Scripts/jquery.js") - .SetCdn("https://code.jquery.com/jquery-3.7.1.min.js", "https://code.jquery.com/jquery-3.7.1.js") - .SetCdnIntegrity("sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs", "sha384-wsqsSADZR1YRBEZ4/kKHNSmU+aX8ojbnKUMN4RyD3jDkxw5mHtoe2z/T/n4l56U/") - .SetVersion("3.7.1"); - - manifest - .DefineScript("jQuery.slim") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery.slim.min.js", "~/OrchardCore.Resources/Scripts/jquery.slim.js") - .SetCdn("https://code.jquery.com/jquery-3.7.1.slim.min.js", "https://code.jquery.com/jquery-3.7.1.slim.js") - .SetCdnIntegrity("sha384-5AkRS45j4ukf+JbWAfHL8P4onPA9p0KwwP7pUdjSQA3ss9edbJUJc/XcYAiheSSz", "sha384-5yyt26go0PtGiMk9qStZt+lySzAg8ZSY0i7q6l05kHEEChYiHvf0NsjlexoEdASI") - .SetVersion("3.7.1"); - - manifest - .DefineScript("jQuery") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.6.0/jquery.min.js", "~/OrchardCore.Resources/Scripts/jquery.js") - .SetCdn("https://code.jquery.com/jquery-3.6.0.min.js", "https://code.jquery.com/jquery-3.6.0.js") - .SetCdnIntegrity("sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK", "sha384-S58meLBGKxIiQmJ/pJ8ilvFUcGcqgla+mWH9EEKGm6i6rKxSTA2kpXJQJ8n7XK4w") - .SetVersion("3.6.0"); - - manifest - .DefineScript("jQuery.slim") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.6.0/jquery.slim.min.js", "~/OrchardCore.Resources/Scripts/jquery.slim.js") - .SetCdn("https://code.jquery.com/jquery-3.6.0.slim.min.js", "https://code.jquery.com/jquery-3.6.0.slim.js") - .SetCdnIntegrity("sha384-Qg00WFl9r0Xr6rUqNLv1ffTSSKEFFCDCKVyHZ+sVt8KuvG99nWw5RNvbhuKgif9z", "sha384-fuUlMletgG/KCb0NwIZTW6aMv/YBbXe0Wt71nwLRreZZpesG/N/aURjEZCG6mtYn") - .SetVersion("3.6.0"); - - manifest - .DefineScript("jQuery") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.js") - .SetCdn("https://code.jquery.com/jquery-3.5.1.min.js", "https://code.jquery.com/jquery-3.5.1.js") - .SetCdnIntegrity("sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2", "sha384-/LjQZzcpTzaYn7qWqRIWYC5l8FWEZ2bIHIz0D73Uzba4pShEcdLdZyZkI4Kv676E") - .SetVersion("3.5.1"); - - manifest - .DefineScript("jQuery.slim") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.slim.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.slim.js") - .SetCdn("https://code.jquery.com/jquery-3.5.1.slim.min.js", "https://code.jquery.com/jquery-3.5.1.slim.js") - .SetCdnIntegrity("sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", "sha384-x6NENSfxadikq2gB4e6/qompriNc+y1J3eqWg3hAAMNBs4dFU303XMTcU3uExJgZ") - .SetVersion("3.5.1"); - - manifest - .DefineScript("jQuery") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.js") - .SetCdn("https://code.jquery.com/jquery-3.4.1.min.js", "https://code.jquery.com/jquery-3.4.1.js") - .SetCdnIntegrity("sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh", "sha384-mlceH9HlqLp7GMKHrj5Ara1+LvdTZVMx4S1U43/NxCvAkzIo8WJ0FE7duLel3wVo") - .SetVersion("3.4.1"); - - manifest - .DefineScript("jQuery.slim") - .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.slim.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.slim.js") - .SetCdn("https://code.jquery.com/jquery-3.4.1.slim.min.js", "https://code.jquery.com/jquery-3.4.1.slim.js") - .SetCdnIntegrity("sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n", "sha384-teRaFq/YbXOM/9FZ1qTavgUgTagWUPsk6xapwcjkrkBHoWvKdZZuAeV8hhaykl+G") - .SetVersion("3.4.1"); - - manifest - .DefineScript("jQuery.easing") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery.easing.min.js", "~/OrchardCore.Resources/Scripts/jquery.easing.js") - .SetCdn("https://cdn.jsdelivr.net/npm/jquery.easing@1.4.1/jquery.easing.min.js", "https://cdn.jsdelivr.net/npm/jquery.easing@1.4.1/jquery.easing.js") - .SetCdnIntegrity("sha384-leGYpHE9Tc4N9OwRd98xg6YFpB9shlc/RkilpFi0ljr3QD4tFoFptZvgnnzzwG4Q", "sha384-fwPA0FyfPOiDsglgAC4ZWmBGwpXSZNkq9IG+cM9HL4CkpNQo4xgCDkOIPdWypLMX") - .SetVersion("1.4.1"); - - manifest - .DefineScript("jQuery-ui") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery-ui.min.js", "~/OrchardCore.Resources/Scripts/jquery-ui.js") - .SetCdn("https://code.jquery.com/ui/1.12.1/jquery-ui.min.js", "https://code.jquery.com/ui/1.12.1/jquery-ui.js") - .SetCdnIntegrity("sha384-Dziy8F2VlJQLMShA6FHWNul/veM9bCkRUaLqr199K94ntO5QUrLJBEbYegdSkkqX", "sha384-JPbtLYL10d/Z1crlc6GGGGM3PavCzzoUJ1UxH0bXHOfguWHQ6XAWrIzW+MBGGXe5") - .SetVersion("1.12.1"); - - manifest - .DefineStyle("jQuery-ui") - .SetUrl("~/OrchardCore.Resources/Styles/jquery-ui.min.css", "~/OrchardCore.Resources/Styles/jquery-ui.css") - .SetCdn("https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.min.css", "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css") - .SetCdnIntegrity("sha384-kcAOn9fN4XSd+TGsNu2OQKSuV5ngOwt7tg73O4EpaD91QXvrfgvf0MR7/2dUjoI6", "sha384-xewr6kSkq3dBbEtB6Z/3oFZmknWn7nHqhLVLrYgzEFRbU/DHSxW7K3B44yWUN60D") - .SetVersion("1.12.1"); - - manifest - .DefineScript("jQuery-ui-i18n") - .SetDependencies("jQuery-ui") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery-ui-i18n.min.js", "~/OrchardCore.Resources/Scripts/jquery-ui-i18n.js") - .SetCdn("https://code.jquery.com/ui/1.7.2/i18n/jquery-ui-i18n.min.js", "https://code.jquery.com/ui/1.7.2/i18n/jquery-ui-i18n.min.js") - .SetCdnIntegrity("sha384-0rV7y4NH7acVmq+7Y9GM6evymvReojk9li+7BYb/ug61uqPSsXJ4uIScVY+N9qtd", "sha384-0rV7y4NH7acVmq+7Y9GM6evymvReojk9li+7BYb/ug61uqPSsXJ4uIScVY+N9qtd") - .SetVersion("1.7.2"); - - manifest - .DefineScript("bootstrap") - .SetDependencies("jQuery") - .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.js") - .SetCdnIntegrity("sha384-vhJnz1OVIdLktyixHY4Uk3OHEwdQqPppqYR8+5mjsauETgLOcEynD9oPHhhz18Nw", "sha384-it0Suwx+VjMafDIVf5t+ozEbrflmNjEddSX5LstI/Xdw3nv4qP/a4e8K4k5hH6l4") - .SetVersion("3.4.0"); - - manifest - .DefineStyle("bootstrap") - .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.css") - .SetCdnIntegrity("sha384-PmY9l28YgO4JwMKbTvgaS7XNZJ30MK9FAZjjzXtlqyZCqBY6X6bXIkM++IkyinN+", "sha384-/5bQ8UYbZnrNY3Mfy6zo9QLgIQD/0CximLKk733r8/pQnXn2mgvhvKhcy43gZtJV") - .SetVersion("3.4.0"); - - manifest - .DefineStyle("bootstrap-theme") - .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap-theme.min.css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap-theme.css") - .SetCdnIntegrity("sha384-jzngWsPS6op3fgRCDTESqrEJwRKck+CILhJVO5VvaAZCq8JYf8HsR/HPpBOOPZfR", "sha384-RtiWe5OsslAYZ9AVyorBziI2VQL7E27rzWygBJh7wrZuVPyK5jeQLLytnJIpJqfD") - .SetVersion("3.4.0"); - - manifest - .DefineScript("popper") - .SetUrl("~/OrchardCore.Resources/Vendor/popper-1.16.1/popper.min.js", "~/OrchardCore.Resources/Vendor/popper-1.16.1/popper.js") - .SetCdn("https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js", "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.js") - .SetCdnIntegrity("sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN", "sha384-cpSm/ilDFOWiMuF2bj03ZzJinb48NO9IGCXcYDtUzdP5y64Ober65chnoOj1XFoA") - .SetVersion("1.16.1"); - - manifest - .DefineScript("bootstrap") - .SetDependencies("jQuery", "popper") - .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.min.js", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.js") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.js") - .SetCdnIntegrity("sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2", "sha384-acUSOMj/FOTIzZ4qpiZXd/6avQezsTkra+wBPeduOyUIA5anC5YcLndJ3Wn4b4pF") - .SetVersion("4.6.1"); - - manifest - .DefineScript("bootstrap-bundle") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.bundle.min.js", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.bundle.js") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.js") - .SetCdnIntegrity("sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF", "sha384-fAPB/5gOv3oOdZZ9/se34OICi8gkYHlNY5skCVFZPzGe+F2puEgM5hu8ctpXyKFM") - .SetVersion("4.6.1"); - - manifest - .DefineStyle("bootstrap") - .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.min.css", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.css") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.css") - .SetCdnIntegrity("sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn", "sha384-ztSeENTvhymkwcI8wMyrHLHIyPJgek5ErHOMw9p96EzJKwbiuJBWBDuPJpGNqOar") - .SetVersion("4.6.1"); - - manifest - .DefineScript("popperjs") - .SetUrl("~/OrchardCore.Resources/Scripts/popper.min.js", "~/OrchardCore.Resources/Scripts/popper.js") - .SetCdn("https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js", "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.js") - .SetCdnIntegrity("sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r", "sha384-yBknSWNrSUPkBtbhhCJ07i/BOmbrigRhLKPzTAny+TT4uGAwIdfNTAkBd/3VzXbg") - .SetVersion("2.11.8"); - - manifest - .DefineScript("bootstrap") - .SetDependencies("popperjs") - .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap.min.js", "~/OrchardCore.Resources/Scripts/bootstrap.js") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.js") - .SetCdnIntegrity("sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy", "sha384-jYoAmFjufJZfBzwTARyz2gk7Jj9mQb2cLeP9n5PcgLCVVd+8QfjY1+qFj+rBkViV") - .SetVersion("5.3.3"); - - manifest - .DefineScript("bootstrap-bundle") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap.bundle.min.js", "~/OrchardCore.Resources/Scripts/bootstrap.bundle.js") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.js") - .SetCdnIntegrity("sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz", "sha384-5xO2n1cyGKAe630nacBqFQxWoXjUIkhoc/FxQrWM07EIZ3TuqkAsusDeyPDOIeid") - .SetVersion("5.3.3"); - - manifest - .DefineStyle("bootstrap") - .SetUrl("~/OrchardCore.Resources/Styles/bootstrap.min.css", "~/OrchardCore.Resources/Styles/bootstrap.css") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.css") - .SetCdnIntegrity("sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH", "sha384-qAlWxD5RDF+aEdUc1Z7GR/tE4zYjX1Igo/LrIexlnzM6G63a6F1fXZWpZKSrSW86") - .SetVersion("5.3.3"); - - manifest - .DefineStyle("bootstrap-rtl") - .SetUrl("~/OrchardCore.Resources/Styles/bootstrap.rtl.min.css", "~/OrchardCore.Resources/Styles/bootstrap.rtl.css") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.css") - .SetCdnIntegrity("sha384-nU14brUcp6StFntEOOEBvcJm4huWjB0OcIeQ3fltAfSmuZFrkAif0T+UtNGlKKQv", "sha384-CEku08bnqQAT/vzi6/zxMQmSyxoOTK1jx7mbT8P7etf/YhPbxASCX5BIVuAK9sfy") - .SetVersion("5.3.3"); - - manifest - .DefineStyle("bootstrap-select") - .SetUrl("~/OrchardCore.Resources/Styles/bootstrap-select.min.css", "~/OrchardCore.Resources/Styles/bootstrap-select.css") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.min.css", "https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.css") - .SetCdnIntegrity("sha384-xF1Y2i6HgC34+4EWddbDhlQuru7cLSKRcPT3hoL3mPoKoV+624vVSZJmegPX77vS", "sha384-DtuOZ7LbR+xAYzDGD4YLpe9eiAayUBwZRqAcoy+RepIoV53tAoJbXnr4AX1xTJ43") - .SetVersion("1.14.0"); - - manifest - .DefineScript("bootstrap-select") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap-select.min.js", "~/OrchardCore.Resources/Scripts/bootstrap-select.js") - .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.min.js", "https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.js") - .SetCdnIntegrity("sha384-0O3sg2SQIGn4393xwamQISjphC8DIXjCzlhj1gPAMC5xGg+2perF5Mehr5njv0fZ", "sha384-2b0aLFg/Ejp4OF57nW0BUqNzm259RHYYMf/mpKClBijsEH2P+4ea2oWAq0twd8L0") - .SetVersion("1.14.0"); - - manifest - .DefineStyle("nouislider") - .SetUrl("~/OrchardCore.Resources/Styles/nouislider.min.css", "~/OrchardCore.Resources/Styles/nouislider.css") - .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.min.css", "https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.css") - .SetCdnIntegrity("sha384-PSZaVsyG9jDu8hFaSJev5s/9poIJlX7cuxSGdqCgXRHpo2DzIaZAyCd2rG/DJJmV", "sha384-SW0/EWtnMakMnwC9RHA27DeNtNCLsJ0l+oZrXlFbb2123lhLdZIbiDiwRPogNY8T") - .SetVersion("15.7.0"); - - manifest - .DefineScript("nouislider") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/nouislider/nouislider.min.js", "~/OrchardCore.Resources/Scripts/nouislider/nouislider.js") - .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.min.js", "https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.js") - .SetCdnIntegrity("sha384-/gBUOLHADjY2rp6bHB0IyW9AC28q4OsnirJScje4l1crgYW7Qarx3dH8zcqcUgmy", "sha384-ZRTsSqAkR2D5UR6P8ew9nDImNmAueqBx3QIljDVMucOjF3eVskkMIk50HUW239mY") - .SetVersion("15.7.0"); - - manifest - .DefineStyle("codemirror") - .SetUrl("~/OrchardCore.Resources/Styles/codemirror/codemirror.min.css", "~/OrchardCore.Resources/Styles/codemirror/codemirror.css") - .SetCdn(CodeMirrorUrl + "codemirror.min.css", CodeMirrorUrl + "codemirror.css") - .SetCdnIntegrity("sha384-zaeBlB/vwYsDRSlFajnDd7OydJ0cWk+c2OWybl3eSUf6hW2EbhlCsQPqKr3gkznT", "sha384-bsaAhvdduZPAwUb7RRLRvDgtEtOsggrgjkr/EjPO1i/vdoi+DmdLaG79UOt6M5hD") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/codemirror.min.js", "~/OrchardCore.Resources/Scripts/codemirror/codemirror.js") - .SetCdn(CodeMirrorUrl + "codemirror.min.js", CodeMirrorUrl + "codemirror.js") - .SetCdnIntegrity("sha384-FIV1f0SplvlwhOxrMYdXpUcnVM79A9u4ffl0lEOVVZunLMuGpxFyQmnBHl24EQFR", "sha384-VAGlhrJE9eY6OkSfl3ioNR4jWUGp1mLeF/ECkHxBUEfTB9u2xVVH14acAUBDc3Y8") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-display-autorefresh") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/display/autorefresh.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/display/autorefresh.js") - .SetCdn(CodeMirrorUrl + "addon/display/autorefresh.min.js", CodeMirrorUrl + "addon/display/autorefresh.js") - .SetCdnIntegrity("sha384-pn83o6MtS8kicn/sV6AhRaBqXQ5tau8NzA2ovcobkcc1uRFP7D8CMhRx231QwKST", "sha384-B1M1WS08oqd1y3zKAPdhkOSNwy+NYMREyK9qXNWl+QfXDlqj+Y+TYuBUkc/uAnox") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineStyle("codemirror-addon-display-fullscreen") - .SetUrl("~/OrchardCore.Resources/Styles/codemirror/addon/display/fullscreen.min.css", "~/OrchardCore.Resources/Styles/codemirror/addon/display/fullscreen.css") - .SetCdn(CodeMirrorUrl + "addon/display/fullscreen.min.css", CodeMirrorUrl + "addon/display/fullscreen.css") - .SetCdnIntegrity("sha384-uuIczW2AGKADJpvg6YiNBJQWE7duDkgQDkndYEsbUGaLm8SPJZzlly6hcEo0aTlW", "sha384-+glu1jsbG+T5ocmkeMvIYh5w07IXKxmJZaCdkNbVfpEr3xi+M0gopFSR/oLKXxio") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-display-fullscreen") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/display/fullscreen.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/display/fullscreen.js") - .SetCdn(CodeMirrorUrl + "addon/display/fullscreen.min.js", CodeMirrorUrl + "addon/display/fullscreen.js") - .SetCdnIntegrity("sha384-mlEZFcWl5HzvZ6rIROEnNm825OC0Gw5KMZkilPtaJL7BGiluUu4c8Ws3IaNauZTh", "sha384-vU/yRPnV0VIhELETYT5fG/k7uMzeHddzkMo4NFrLzHdJepeb46v0b61P8FADXtN4") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-edit-closetag") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/edit/closetag.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/edit/closetag.js") - .SetCdn(CodeMirrorUrl + "addon/edit/closetag.min.js", CodeMirrorUrl + "addon/edit/closetag.js") - .SetCdnIntegrity("sha384-WIyvbwMte2q6VXgBkN7prVo9ZSmBm47nI2ftVjoJLPY4yOu9gmI6lqaNXjBwHU5k", "sha384-i4ai3UXE5wIk3ILN77PB9DhNmku+sefNKDTHXRvsrYX2bxWzm+EDmoBui5wsNU2v") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineStyle("codemirror-addon-hint-show-hint") - .SetUrl("~/OrchardCore.Resources/Styles/codemirror/addon/hint/show-hint.min.css", "~/OrchardCore.Resources/Styles/codemirror/addon/hint/show-hint.css") - .SetCdn(CodeMirrorUrl + "addon/hint/show-hint.min.css", CodeMirrorUrl + "addon/hint/show-hint.css") - .SetCdnIntegrity("sha384-qqTWkykzuDLx4yDYa7bVrwNwBHuqVvklDUMVaU4eezgNUEgGbP8Zv6i3u8OmtuWg", "sha384-ZZbLvEvLoXKrHo3Tkh7W8amMgoHFkDzWe8IAm1ZgxsG5y35H+fJCVMWwr0YBAEGA") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-hint-show-hint") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/hint/show-hint.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/hint/show-hint.js") - .SetCdn(CodeMirrorUrl + "addon/hint/show-hint.min.js", CodeMirrorUrl + "addon/hint/show-hint.js") - .SetCdnIntegrity("sha384-8i/ZCyq/QakicQDFEJhSl5oJEzzChdhEHhFBvtlxJMWdzohX6j+7O7L7NGgVjaL2", "sha384-X99dMoDW1QAOqOa49NoR3mVQPZMAmWdk1EtGVfWHdqFrD0mKdc4TVCugISfW0wEv") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-hint-sql-hint") - .SetDependencies("codemirror-addon-hint-show-hint") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/hint/sql-hint.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/hint/sql-hint.js") - .SetCdn(CodeMirrorUrl + "addon/hint/sql-hint.min.js", CodeMirrorUrl + "addon/hint/sql-hint.js") - .SetCdnIntegrity("sha384-TT6FzU/qfXKsyGpknyPBSW9YUv9boLL9TStdfYRJTMjLDdUaQwWceBVs8I326z16", "sha384-v0PZIWVaXK+SdJWDH/8f3lMh+4SXRujsh67aPj27BlUq4ocyn0Yime8qyi8AArtz") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-mode-multiplex") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/mode/multiplex.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/mode/multiplex.js") - .SetCdn(CodeMirrorUrl + "addon/mode/multiplex.min.js", CodeMirrorUrl + "addon/mode/multiplex.js") - .SetCdnIntegrity("sha384-Qr/1hjoJmzEf6ToQLo1nr8/GF5ekpaqUx0DM71QHZ4N8+c8l8aqlQ25whgApmChE", "sha384-pgj6NfGJNeIBDFqYM8m/ah3J269WhaJDiT0fN/C0c335qDMJAbfz/O2rVB53w+1D") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-mode-simple") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/mode/simple.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/mode/simple.js") - .SetCdn(CodeMirrorUrl + "addon/mode/simple.min.js", CodeMirrorUrl + "addon/mode/simple.js") - .SetCdnIntegrity("sha384-5+aYjV0V2W3IwhAYp/9WOrGMv1TaYkCjnkkW7Hv3yJQo28MergRCSRaUIUzDUs2J", "sha384-qeu+SDWpTAqXUyXdqdwbMDeYSQiv6rErEHLJ/cITY3wtuYhN2LdBSBZeS7Jyn8nv") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-addon-selection-active-line") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/selection/active-line.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/selection/active-line.js") - .SetCdn(CodeMirrorUrl + "addon/selection/active-line.min.js", CodeMirrorUrl + "addon/selection/active-line.js") - .SetCdnIntegrity("sha384-hcxaXyAtJ30s2NeDu1OHWsQRiHiWuYLTbI596+YFb+f2pFhzO0mDuahZziRPPDxg", "sha384-QhD10EHRrst6CIOzeEBXQhUT95YVJN1EV8uX2Jb5S3+qw/ozbvUE5Zn5uILnZg96") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-mode-css") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/css/css.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/css/css.js") - .SetCdn(CodeMirrorUrl + "mode/css/css.min.js", CodeMirrorUrl + "mode/css/css.js") - .SetCdnIntegrity("sha384-fpeIC2FZuPmw7mIsTvgB5BNc8QVxQC/nWg2W+CgPYOAiBiYVuHe2E8HiTWHBMIJQ", "sha384-ZD4C1ohrucZOfP7+jQSuBELICO7Z73CFD5stbjic1D3DbZk88mqj3KsRjSml/NCK") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-mode-htmlmixed") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/htmlmixed/htmlmixed.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/htmlmixed/htmlmixed.js") - .SetCdn(CodeMirrorUrl + "mode/htmlmixed/htmlmixed.min.js", CodeMirrorUrl + "mode/htmlmixed/htmlmixed.js") - .SetCdnIntegrity("sha384-xYIbc5F55vPi7pb/lUnFj3wu24HlpAMZdtBHkNrb2YhPzJV3pX7+eqXT2PXSNMrw", "sha384-0MH/N0DfWPIbCXDe9I7tmLw0dsJ4gKQUijwCcpaTgGxrTTrypsJwEOOqi5yhkQiK") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-mode-javascript") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/javascript/javascript.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/javascript/javascript.js") - .SetCdn(CodeMirrorUrl + "mode/javascript/javascript.min.js", CodeMirrorUrl + "mode/javascript/javascript.js") - .SetCdnIntegrity("sha384-kmQrbJf09Uo1WRLMDVGoVG3nM6F48frIhcj7f3FDUjeRzsiHwyBWDjMUIttnIeAf", "sha384-fgstVTG7RpZvquT2ZtQeU9iJXfvK9wTstlhasVKML77E5oe3Wi0AfLzY2grsjwmh") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-mode-sql") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/sql/sql.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/sql/sql.js") - .SetCdn(CodeMirrorUrl + "mode/sql/sql.min.js", CodeMirrorUrl + "mode/sql/sql.js") - .SetCdnIntegrity("sha384-cof/65v3Fn+7YeqmMp8aCI1xZ2yeX8JnWhQYdMzaO7VDpBOtLjfdwx6pEfUo7SFT", "sha384-Llr4/OHf89ufsfG7HbSwqB7KTd1d4hXok/Yxi72qWvLwtd6pz6nqJdOGlDdpjw6I") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineScript("codemirror-mode-xml") - .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/xml/xml.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/xml/xml.js") - .SetCdn(CodeMirrorUrl + "mode/xml/xml.min.js", CodeMirrorUrl + "mode/xml/xml.js") - .SetCdnIntegrity("sha384-xPpkMo5nDgD98fIcuRVYhxkZV6/9Y4L8s3p0J5c4MxgJkyKJ8BJr+xfRkq7kn6Tw", "sha384-KX/+LdYWr3JcKfT7HK55DC3oPVJwnJSympb1qoO14sxVDtDIg+xHPVLltqJEbitI") - .SetVersion(CodeMirrorVersion); - - manifest - .DefineStyle("font-awesome") - .SetUrl("~/OrchardCore.Resources/Styles/font-awesome.min.css", "~/OrchardCore.Resources/Styles/font-awesome.css") - .SetCdn("https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css", "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css") - .SetCdnIntegrity("sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN", "sha384-FckWOBo7yuyMS7In0aXZ0aoVvnInlnFMwCv77x9sZpFgOonQgnBj1uLwenWVtsEj") - .SetVersion("4.7.0"); - - manifest - .DefineStyle("font-awesome") - .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.min.css", "~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.css") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.css") - .SetCdnIntegrity("sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm", "sha384-7rgjkhkxJ95zOzIjk97UrBOe14KgYpH9+zQm5BdgzjQELBU6kHf4WwoQzHfTx5sw") - .SetVersion("5.15.4"); - - manifest - .DefineScript("font-awesome") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/all.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/all.js") - .SetCdnIntegrity("sha384-rOA1PnstxnOBLzCLMcre8ybwbTmemjzdNlILg8O7z1lUkLXozs4DHonlDtnE7fpc", "sha384-HfU7cInvKb8zxQuLKtKr/suuRgcSH1OYsdJU+8lGA/t8nyNgdJF09UIkRzg1iefj") - .SetVersion("5.15.4"); - - manifest - .DefineScript("font-awesome-v4-shims") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/v4-shims.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/v4-shims.js") - .SetCdnIntegrity("sha384-bx00wqJq+zY9QLCMa/zViZPu1f0GJ3VXwF4GSw3GbfjwO28QCFr4qadCrNmJQ/9N", "sha384-SGuqaGE4bcW7Xl5T06BsUPUA91qaNtT53uGOcGpavQMje3goIFJbDsC0VAwtgL5g") - .SetVersion("5.15.4"); - - manifest - .DefineStyle("font-awesome") - .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.min.css", "~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.css") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.min.css", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.css") - .SetCdnIntegrity("sha384-h/hnnw1Bi4nbpD6kE7nYfCXzovi622sY5WBxww8ARKwpdLj5kUWjRuyiXaD1U2JT", "sha384-vMdawx0r3BjxHQwcfWi0YSemtW6u5mKxTKPPh1ogUICPLaEa/6e42yg2wRYzzJtx") - .SetVersion("6.6.0"); - - manifest - .DefineScript("font-awesome") - .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/js/all.min.js", "~/OrchardCore.Resources/Vendor/fontawesome-free/js/all.js") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.js") - .SetCdnIntegrity("sha384-dgEl3vRKux81M373f/TdgoDTV5oZj+yjHrr/1qR5b4btG5q63kYS62t5kod+7Q6v", "sha384-v72QeVpDL6Ne4X2S+fwIXCOLhO57lwIR/qRV05SHAExDg1QoyJGWDxx6VQO3rzlC") - .SetVersion("6.6.0"); - - manifest - .DefineScript("font-awesome-v4-shims") - .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/js/v4-shims.min.js", "~/OrchardCore.Resources/Vendor/fontawesome-free/js/v4-shims.js") - .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/v4-shims.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/v4-shims.js") - .SetCdnIntegrity("sha384-M9y++reQwf5nddw5loUHChCbGE4kwaeHzeEM2yWidMfaRMQeHM6MSwwPuiSnSMHF", "sha384-WrIndr6nwB5l/sVUyZkWDlpNS9Vx/Y+zhmjZNP+j2UJOBmy36ufaIIqd7o2kL0BL") - .SetVersion("6.6.0"); - - manifest - .DefineScript("jquery-resizable") - .SetDependencies("jQuery") - .SetUrl("~/OrchardCore.Resources/Scripts/jquery-resizable.min.js", "~/OrchardCore.Resources/Scripts/jquery-resizable.js") - .SetCdn("https://cdn.jsdelivr.net/npm/jquery-resizable-dom@0.35.0/dist/jquery-resizable.min.js") - .SetCdnIntegrity("sha384-1LMjDEezsSgzlRgsyFIAvLW7FWSdFIHqBGjUa+ad5EqtK1FORC8XpTJ/pahxj5GB", "sha384-0yk9X0IG0cXxuN9yTTkps/3TNNI9ZcaKKhh8dgqOEAWGXxIYS5xaY2as6b32Ov3P") - .SetVersion("0.35.0"); - - manifest - .DefineStyle("trumbowyg") - .SetUrl("~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg.min.css", "~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg.css") - .SetCdn("https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/ui/trumbowyg.min.css", "https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/ui/trumbowyg.css") - .SetCdnIntegrity("sha384-XfI6P0jtm0X3QDEQxS1DotzhIXkJeuSV1wtBOntPaRxzpzTkMWkpZpkuzj8qVHzl", "sha384-GUyfWhYsIKKkAMejJuy50VTSfyfkrrJX2csg7fxJJt7vi+gXH8qxqH29C5GURaum") - .SetVersion("2.28.0"); - - manifest - .DefineScript("trumbowyg") - .SetDependencies("jquery-resizable") - .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.js") - .SetCdn("https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/trumbowyg.min.js", "https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/trumbowyg.js") - .SetCdnIntegrity("sha384-O4OAGMDq5hnqt4/WQz+fW6yVgZ02jmw+Yf1j02zIgglnCYXf/7TmET8tFbrTN6u5", "sha384-dNxlebCuuiNWPhPBEd69nEAtkEWa7Z9IWkrL+OSmJ456dlu6TAASXgL72Bn4GGju") - .SetVersion("2.28.0"); - - manifest - .DefineScript("trumbowyg-shortcodes") - .SetDependencies("trumbowyg") - .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.shortcodes.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.shortcodes.js") - .SetVersion("1.0.0"); - - manifest - .DefineScript("trumbowyg-theme") - .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.theme.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.theme.js") - .SetDependencies("trumbowyg", "theme-head") - .SetVersion("1.0.0"); - - manifest - .DefineStyle("trumbowyg-plugins") - .SetDependencies("trumbowyg") - .SetUrl("~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg-plugins.min.css", "~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg-plugins.css") - .SetVersion("2.28.0"); - - manifest - .DefineScript("trumbowyg-plugins") - .SetDependencies("trumbowyg") - .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg-plugins.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg-plugins.js") - .SetVersion("2.28.0"); - - manifest - .DefineScript("credential-helpers") - .SetUrl("~/OrchardCore.Resources/Scripts/credential-helpers.min.js", "~/OrchardCore.Resources/Scripts/credential-helpers.js") - .SetVersion("1.0.0"); - - manifest - .DefineScript("vuejs") - .SetUrl("~/OrchardCore.Resources/Scripts/vue.min.js", "~/OrchardCore.Resources/Scripts/vue.js") - .SetCdn("https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js", "https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js") - .SetCdnIntegrity("sha384-ULpZhk1pvhc/UK5ktA9kwb2guy9ovNSTyxPNHANnA35YjBQgdwI+AhLkixDvdlw4", "sha384-t1tHLsbM7bYMJCXlhr0//00jSs7ZhsAhxgm191xFsyzvieTMCbUWKMhFg9I6ci8q") - .SetVersion("2.6.14"); - - manifest - .DefineScript("vue-multiselect") - .SetDependencies("vuejs") - .SetUrl("~/OrchardCore.Resources/Scripts/vue-multiselect.min.js", "~/OrchardCore.Resources/Scripts/vue-multiselect.min.js") - .SetCdn("https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.js", "https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.js") - .SetCdnIntegrity("sha384-a4eXewRTYCwYdFtSnMCZTNtiXrfdul6aQdueRgHPAx2y1Ldp0QaFdCTpOx0ycsXU", "sha384-a4eXewRTYCwYdFtSnMCZTNtiXrfdul6aQdueRgHPAx2y1Ldp0QaFdCTpOx0ycsXU") - .SetVersion("2.1.6"); - - manifest - .DefineStyle("vue-multiselect") - .SetUrl("~/OrchardCore.Resources/Styles/vue-multiselect.min.css", "~/OrchardCore.Resources/Styles/vue-multiselect.min.css") - .SetCdn("https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.css", "https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.css") - .SetCdnIntegrity("sha384-PPH/T7V86Z1+B4eMPef4FJXLD5fsTpObWoCoK3CiNtSX7aji+5qxpOCn1f2TDYAM", "sha384-PPH/T7V86Z1+B4eMPef4FJXLD5fsTpObWoCoK3CiNtSX7aji+5qxpOCn1f2TDYAM") - .SetVersion("2.1.6"); - - manifest - .DefineScript("Sortable") - .SetUrl("~/OrchardCore.Resources/Scripts/Sortable.min.js", "~/OrchardCore.Resources/Scripts/Sortable.js") - .SetCdn("https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js", "https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.js") - .SetCdnIntegrity("sha384-eeLEhtwdMwD3X9y+8P3Cn7Idl/M+w8H4uZqkgD/2eJVkWIN1yKzEj6XegJ9dL3q0", "sha384-OFIl93h6jYoAF+hATXncsLYMiLo81FpuReH3fgCI4wep7qNCmiA2I0bwcvVqHSBj") - .SetVersion("1.15.0"); - - manifest - .DefineScript("vuedraggable") - .SetDependencies("vuejs", "Sortable") - .SetUrl("~/OrchardCore.Resources/Scripts/vuedraggable.umd.min.js", "~/OrchardCore.Resources/Scripts/vuedraggable.umd.js") - .SetCdn("https://cdn.jsdelivr.net/npm/vuedraggable@2.24.3/dist/vuedraggable.umd.min.js", "https://cdn.jsdelivr.net/npm/vuedraggable@2.24.3/dist/vuedraggable.umd.js") - .SetCdnIntegrity("sha384-qUA1xXJiX23E4GOeW/XHtsBkV9MUcHLSjhi3FzO08mv8+W8bv5AQ1cwqLskycOTs", "sha384-+jB9vXc/EaIJTlNiZG2tv+TUpKm6GR9HCRZb3VkI3lscZWqrCYDbX2ZXffNJldL9") - .SetVersion("2.24.3"); - - manifest - .DefineScript("js-cookie") - .SetUrl("~/OrchardCore.Resources/Scripts/js.cookie.min.js", "~/OrchardCore.Resources/Scripts/js.cookie.js") - .SetCdn("https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js", "https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.js") - .SetCdnIntegrity("sha384-/vxhYfM1LENRhdpZ8dwEsQn/X4VhpbEZSiU4m/FwR+PVpzar4fkEOw8FP9Y+OfQN", "sha384-b1TD0tFP+Ao4jmFaQw9RQxezUooFrLdlqfDfoh1SKv5L3jG7dD44QiwD+UzckH8W") - .SetVersion("3.0.5"); - - manifest - .DefineScript("monaco-loader") - .SetUrl("~/OrchardCore.Resources/Scripts/monaco/vs/loader.js") - .SetPosition(ResourcePosition.Last) - .SetVersion(MonacoEditorVersion); - - manifest - .DefineScript("monaco") - .SetAttribute("data-tenant-prefix", _pathBase) - .SetUrl("~/OrchardCore.Resources/Scripts/monaco/ocmonaco.js") - .SetDependencies("monaco-loader") - .SetVersion(MonacoEditorVersion); - - return manifest; - } + ResourceManifest BuildManifest() + { + var manifest = new ResourceManifest(); + + manifest + .DefineScript("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery.min.js", "~/OrchardCore.Resources/Scripts/jquery.js") + .SetCdn("https://code.jquery.com/jquery-3.7.1.min.js", "https://code.jquery.com/jquery-3.7.1.js") + .SetCdnIntegrity("sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs", "sha384-wsqsSADZR1YRBEZ4/kKHNSmU+aX8ojbnKUMN4RyD3jDkxw5mHtoe2z/T/n4l56U/") + .SetVersion("3.7.1"); + + manifest + .DefineScript("jQuery.slim") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery.slim.min.js", "~/OrchardCore.Resources/Scripts/jquery.slim.js") + .SetCdn("https://code.jquery.com/jquery-3.7.1.slim.min.js", "https://code.jquery.com/jquery-3.7.1.slim.js") + .SetCdnIntegrity("sha384-5AkRS45j4ukf+JbWAfHL8P4onPA9p0KwwP7pUdjSQA3ss9edbJUJc/XcYAiheSSz", "sha384-5yyt26go0PtGiMk9qStZt+lySzAg8ZSY0i7q6l05kHEEChYiHvf0NsjlexoEdASI") + .SetVersion("3.7.1"); + + manifest + .DefineScript("jQuery") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.6.0/jquery.min.js", "~/OrchardCore.Resources/Scripts/jquery.js") + .SetCdn("https://code.jquery.com/jquery-3.6.0.min.js", "https://code.jquery.com/jquery-3.6.0.js") + .SetCdnIntegrity("sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK", "sha384-S58meLBGKxIiQmJ/pJ8ilvFUcGcqgla+mWH9EEKGm6i6rKxSTA2kpXJQJ8n7XK4w") + .SetVersion("3.6.0"); + + manifest + .DefineScript("jQuery.slim") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.6.0/jquery.slim.min.js", "~/OrchardCore.Resources/Scripts/jquery.slim.js") + .SetCdn("https://code.jquery.com/jquery-3.6.0.slim.min.js", "https://code.jquery.com/jquery-3.6.0.slim.js") + .SetCdnIntegrity("sha384-Qg00WFl9r0Xr6rUqNLv1ffTSSKEFFCDCKVyHZ+sVt8KuvG99nWw5RNvbhuKgif9z", "sha384-fuUlMletgG/KCb0NwIZTW6aMv/YBbXe0Wt71nwLRreZZpesG/N/aURjEZCG6mtYn") + .SetVersion("3.6.0"); + + manifest + .DefineScript("jQuery") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.js") + .SetCdn("https://code.jquery.com/jquery-3.5.1.min.js", "https://code.jquery.com/jquery-3.5.1.js") + .SetCdnIntegrity("sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2", "sha384-/LjQZzcpTzaYn7qWqRIWYC5l8FWEZ2bIHIz0D73Uzba4pShEcdLdZyZkI4Kv676E") + .SetVersion("3.5.1"); + + manifest + .DefineScript("jQuery.slim") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.slim.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.5.1/jquery.slim.js") + .SetCdn("https://code.jquery.com/jquery-3.5.1.slim.min.js", "https://code.jquery.com/jquery-3.5.1.slim.js") + .SetCdnIntegrity("sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", "sha384-x6NENSfxadikq2gB4e6/qompriNc+y1J3eqWg3hAAMNBs4dFU303XMTcU3uExJgZ") + .SetVersion("3.5.1"); + + manifest + .DefineScript("jQuery") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.js") + .SetCdn("https://code.jquery.com/jquery-3.4.1.min.js", "https://code.jquery.com/jquery-3.4.1.js") + .SetCdnIntegrity("sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh", "sha384-mlceH9HlqLp7GMKHrj5Ara1+LvdTZVMx4S1U43/NxCvAkzIo8WJ0FE7duLel3wVo") + .SetVersion("3.4.1"); + + manifest + .DefineScript("jQuery.slim") + .SetUrl("~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.slim.min.js", "~/OrchardCore.Resources/Vendor/jquery-3.4.1/jquery.slim.js") + .SetCdn("https://code.jquery.com/jquery-3.4.1.slim.min.js", "https://code.jquery.com/jquery-3.4.1.slim.js") + .SetCdnIntegrity("sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n", "sha384-teRaFq/YbXOM/9FZ1qTavgUgTagWUPsk6xapwcjkrkBHoWvKdZZuAeV8hhaykl+G") + .SetVersion("3.4.1"); + + manifest + .DefineScript("jQuery.easing") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery.easing.min.js", "~/OrchardCore.Resources/Scripts/jquery.easing.js") + .SetCdn("https://cdn.jsdelivr.net/npm/jquery.easing@1.4.1/jquery.easing.min.js", "https://cdn.jsdelivr.net/npm/jquery.easing@1.4.1/jquery.easing.js") + .SetCdnIntegrity("sha384-leGYpHE9Tc4N9OwRd98xg6YFpB9shlc/RkilpFi0ljr3QD4tFoFptZvgnnzzwG4Q", "sha384-fwPA0FyfPOiDsglgAC4ZWmBGwpXSZNkq9IG+cM9HL4CkpNQo4xgCDkOIPdWypLMX") + .SetVersion("1.4.1"); + + manifest + .DefineScript("jQuery-ui") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery-ui.min.js", "~/OrchardCore.Resources/Scripts/jquery-ui.js") + .SetCdn("https://code.jquery.com/ui/1.12.1/jquery-ui.min.js", "https://code.jquery.com/ui/1.12.1/jquery-ui.js") + .SetCdnIntegrity("sha384-Dziy8F2VlJQLMShA6FHWNul/veM9bCkRUaLqr199K94ntO5QUrLJBEbYegdSkkqX", "sha384-JPbtLYL10d/Z1crlc6GGGGM3PavCzzoUJ1UxH0bXHOfguWHQ6XAWrIzW+MBGGXe5") + .SetVersion("1.12.1"); + + manifest + .DefineStyle("jQuery-ui") + .SetUrl("~/OrchardCore.Resources/Styles/jquery-ui.min.css", "~/OrchardCore.Resources/Styles/jquery-ui.css") + .SetCdn("https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.min.css", "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css") + .SetCdnIntegrity("sha384-kcAOn9fN4XSd+TGsNu2OQKSuV5ngOwt7tg73O4EpaD91QXvrfgvf0MR7/2dUjoI6", "sha384-xewr6kSkq3dBbEtB6Z/3oFZmknWn7nHqhLVLrYgzEFRbU/DHSxW7K3B44yWUN60D") + .SetVersion("1.12.1"); + + manifest + .DefineScript("jQuery-ui-i18n") + .SetDependencies("jQuery-ui") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery-ui-i18n.min.js", "~/OrchardCore.Resources/Scripts/jquery-ui-i18n.js") + .SetCdn("https://code.jquery.com/ui/1.7.2/i18n/jquery-ui-i18n.min.js", "https://code.jquery.com/ui/1.7.2/i18n/jquery-ui-i18n.min.js") + .SetCdnIntegrity("sha384-0rV7y4NH7acVmq+7Y9GM6evymvReojk9li+7BYb/ug61uqPSsXJ4uIScVY+N9qtd", "sha384-0rV7y4NH7acVmq+7Y9GM6evymvReojk9li+7BYb/ug61uqPSsXJ4uIScVY+N9qtd") + .SetVersion("1.7.2"); + + manifest + .DefineScript("bootstrap") + .SetDependencies("jQuery") + .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.js") + .SetCdnIntegrity("sha384-vhJnz1OVIdLktyixHY4Uk3OHEwdQqPppqYR8+5mjsauETgLOcEynD9oPHhhz18Nw", "sha384-it0Suwx+VjMafDIVf5t+ozEbrflmNjEddSX5LstI/Xdw3nv4qP/a4e8K4k5hH6l4") + .SetVersion("3.4.0"); + + manifest + .DefineStyle("bootstrap") + .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.css") + .SetCdnIntegrity("sha384-PmY9l28YgO4JwMKbTvgaS7XNZJ30MK9FAZjjzXtlqyZCqBY6X6bXIkM++IkyinN+", "sha384-/5bQ8UYbZnrNY3Mfy6zo9QLgIQD/0CximLKk733r8/pQnXn2mgvhvKhcy43gZtJV") + .SetVersion("3.4.0"); + + manifest + .DefineStyle("bootstrap-theme") + .SetCdn("https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap-theme.min.css", "https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap-theme.css") + .SetCdnIntegrity("sha384-jzngWsPS6op3fgRCDTESqrEJwRKck+CILhJVO5VvaAZCq8JYf8HsR/HPpBOOPZfR", "sha384-RtiWe5OsslAYZ9AVyorBziI2VQL7E27rzWygBJh7wrZuVPyK5jeQLLytnJIpJqfD") + .SetVersion("3.4.0"); + + manifest + .DefineScript("popper") + .SetUrl("~/OrchardCore.Resources/Vendor/popper-1.16.1/popper.min.js", "~/OrchardCore.Resources/Vendor/popper-1.16.1/popper.js") + .SetCdn("https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js", "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.js") + .SetCdnIntegrity("sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN", "sha384-cpSm/ilDFOWiMuF2bj03ZzJinb48NO9IGCXcYDtUzdP5y64Ober65chnoOj1XFoA") + .SetVersion("1.16.1"); + + manifest + .DefineScript("bootstrap") + .SetDependencies("jQuery", "popper") + .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.min.js", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.js") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.js") + .SetCdnIntegrity("sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2", "sha384-acUSOMj/FOTIzZ4qpiZXd/6avQezsTkra+wBPeduOyUIA5anC5YcLndJ3Wn4b4pF") + .SetVersion("4.6.1"); + + manifest + .DefineScript("bootstrap-bundle") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.bundle.min.js", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.bundle.js") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.js") + .SetCdnIntegrity("sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF", "sha384-fAPB/5gOv3oOdZZ9/se34OICi8gkYHlNY5skCVFZPzGe+F2puEgM5hu8ctpXyKFM") + .SetVersion("4.6.1"); + + manifest + .DefineStyle("bootstrap") + .SetUrl("~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.min.css", "~/OrchardCore.Resources/Vendor/bootstrap-4.6.1/bootstrap.css") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.css") + .SetCdnIntegrity("sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn", "sha384-ztSeENTvhymkwcI8wMyrHLHIyPJgek5ErHOMw9p96EzJKwbiuJBWBDuPJpGNqOar") + .SetVersion("4.6.1"); + + manifest + .DefineScript("popperjs") + .SetUrl("~/OrchardCore.Resources/Scripts/popper.min.js", "~/OrchardCore.Resources/Scripts/popper.js") + .SetCdn("https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js", "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.js") + .SetCdnIntegrity("sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r", "sha384-yBknSWNrSUPkBtbhhCJ07i/BOmbrigRhLKPzTAny+TT4uGAwIdfNTAkBd/3VzXbg") + .SetVersion("2.11.8"); + + manifest + .DefineScript("bootstrap") + .SetDependencies("popperjs") + .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap.min.js", "~/OrchardCore.Resources/Scripts/bootstrap.js") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.js") + .SetCdnIntegrity("sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy", "sha384-jYoAmFjufJZfBzwTARyz2gk7Jj9mQb2cLeP9n5PcgLCVVd+8QfjY1+qFj+rBkViV") + .SetVersion("5.3.3"); + + manifest + .DefineScript("bootstrap-bundle") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap.bundle.min.js", "~/OrchardCore.Resources/Scripts/bootstrap.bundle.js") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.js") + .SetCdnIntegrity("sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz", "sha384-5xO2n1cyGKAe630nacBqFQxWoXjUIkhoc/FxQrWM07EIZ3TuqkAsusDeyPDOIeid") + .SetVersion("5.3.3"); + + manifest + .DefineStyle("bootstrap") + .SetUrl("~/OrchardCore.Resources/Styles/bootstrap.min.css", "~/OrchardCore.Resources/Styles/bootstrap.css") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.css") + .SetCdnIntegrity("sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH", "sha384-qAlWxD5RDF+aEdUc1Z7GR/tE4zYjX1Igo/LrIexlnzM6G63a6F1fXZWpZKSrSW86") + .SetVersion("5.3.3"); + + manifest + .DefineStyle("bootstrap-rtl") + .SetUrl("~/OrchardCore.Resources/Styles/bootstrap.rtl.min.css", "~/OrchardCore.Resources/Styles/bootstrap.rtl.css") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css", "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.css") + .SetCdnIntegrity("sha384-nU14brUcp6StFntEOOEBvcJm4huWjB0OcIeQ3fltAfSmuZFrkAif0T+UtNGlKKQv", "sha384-CEku08bnqQAT/vzi6/zxMQmSyxoOTK1jx7mbT8P7etf/YhPbxASCX5BIVuAK9sfy") + .SetVersion("5.3.3"); + + manifest + .DefineStyle("bootstrap-select") + .SetUrl("~/OrchardCore.Resources/Styles/bootstrap-select.min.css", "~/OrchardCore.Resources/Styles/bootstrap-select.css") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.min.css", "https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/css/bootstrap-select.css") + .SetCdnIntegrity("sha384-xF1Y2i6HgC34+4EWddbDhlQuru7cLSKRcPT3hoL3mPoKoV+624vVSZJmegPX77vS", "sha384-DtuOZ7LbR+xAYzDGD4YLpe9eiAayUBwZRqAcoy+RepIoV53tAoJbXnr4AX1xTJ43") + .SetVersion("1.14.0"); + + manifest + .DefineScript("bootstrap-select") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/bootstrap-select.min.js", "~/OrchardCore.Resources/Scripts/bootstrap-select.js") + .SetCdn("https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.min.js", "https://cdn.jsdelivr.net/npm/bootstrap-select@1.14.0-beta3/dist/js/bootstrap-select.js") + .SetCdnIntegrity("sha384-0O3sg2SQIGn4393xwamQISjphC8DIXjCzlhj1gPAMC5xGg+2perF5Mehr5njv0fZ", "sha384-2b0aLFg/Ejp4OF57nW0BUqNzm259RHYYMf/mpKClBijsEH2P+4ea2oWAq0twd8L0") + .SetVersion("1.14.0"); + + manifest + .DefineStyle("nouislider") + .SetUrl("~/OrchardCore.Resources/Styles/nouislider.min.css", "~/OrchardCore.Resources/Styles/nouislider.css") + .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.min.css", "https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.css") + .SetCdnIntegrity("sha384-PSZaVsyG9jDu8hFaSJev5s/9poIJlX7cuxSGdqCgXRHpo2DzIaZAyCd2rG/DJJmV", "sha384-SW0/EWtnMakMnwC9RHA27DeNtNCLsJ0l+oZrXlFbb2123lhLdZIbiDiwRPogNY8T") + .SetVersion("15.7.0"); + + manifest + .DefineScript("nouislider") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/nouislider/nouislider.min.js", "~/OrchardCore.Resources/Scripts/nouislider/nouislider.js") + .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.min.js", "https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.0/nouislider.js") + .SetCdnIntegrity("sha384-/gBUOLHADjY2rp6bHB0IyW9AC28q4OsnirJScje4l1crgYW7Qarx3dH8zcqcUgmy", "sha384-ZRTsSqAkR2D5UR6P8ew9nDImNmAueqBx3QIljDVMucOjF3eVskkMIk50HUW239mY") + .SetVersion("15.7.0"); + + manifest + .DefineStyle("codemirror") + .SetUrl("~/OrchardCore.Resources/Styles/codemirror/codemirror.min.css", "~/OrchardCore.Resources/Styles/codemirror/codemirror.css") + .SetCdn(CodeMirrorUrl + "codemirror.min.css", CodeMirrorUrl + "codemirror.css") + .SetCdnIntegrity("sha384-zaeBlB/vwYsDRSlFajnDd7OydJ0cWk+c2OWybl3eSUf6hW2EbhlCsQPqKr3gkznT", "sha384-bsaAhvdduZPAwUb7RRLRvDgtEtOsggrgjkr/EjPO1i/vdoi+DmdLaG79UOt6M5hD") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/codemirror.min.js", "~/OrchardCore.Resources/Scripts/codemirror/codemirror.js") + .SetCdn(CodeMirrorUrl + "codemirror.min.js", CodeMirrorUrl + "codemirror.js") + .SetCdnIntegrity("sha384-FIV1f0SplvlwhOxrMYdXpUcnVM79A9u4ffl0lEOVVZunLMuGpxFyQmnBHl24EQFR", "sha384-VAGlhrJE9eY6OkSfl3ioNR4jWUGp1mLeF/ECkHxBUEfTB9u2xVVH14acAUBDc3Y8") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-display-autorefresh") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/display/autorefresh.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/display/autorefresh.js") + .SetCdn(CodeMirrorUrl + "addon/display/autorefresh.min.js", CodeMirrorUrl + "addon/display/autorefresh.js") + .SetCdnIntegrity("sha384-pn83o6MtS8kicn/sV6AhRaBqXQ5tau8NzA2ovcobkcc1uRFP7D8CMhRx231QwKST", "sha384-B1M1WS08oqd1y3zKAPdhkOSNwy+NYMREyK9qXNWl+QfXDlqj+Y+TYuBUkc/uAnox") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineStyle("codemirror-addon-display-fullscreen") + .SetUrl("~/OrchardCore.Resources/Styles/codemirror/addon/display/fullscreen.min.css", "~/OrchardCore.Resources/Styles/codemirror/addon/display/fullscreen.css") + .SetCdn(CodeMirrorUrl + "addon/display/fullscreen.min.css", CodeMirrorUrl + "addon/display/fullscreen.css") + .SetCdnIntegrity("sha384-uuIczW2AGKADJpvg6YiNBJQWE7duDkgQDkndYEsbUGaLm8SPJZzlly6hcEo0aTlW", "sha384-+glu1jsbG+T5ocmkeMvIYh5w07IXKxmJZaCdkNbVfpEr3xi+M0gopFSR/oLKXxio") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-display-fullscreen") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/display/fullscreen.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/display/fullscreen.js") + .SetCdn(CodeMirrorUrl + "addon/display/fullscreen.min.js", CodeMirrorUrl + "addon/display/fullscreen.js") + .SetCdnIntegrity("sha384-mlEZFcWl5HzvZ6rIROEnNm825OC0Gw5KMZkilPtaJL7BGiluUu4c8Ws3IaNauZTh", "sha384-vU/yRPnV0VIhELETYT5fG/k7uMzeHddzkMo4NFrLzHdJepeb46v0b61P8FADXtN4") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-edit-closetag") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/edit/closetag.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/edit/closetag.js") + .SetCdn(CodeMirrorUrl + "addon/edit/closetag.min.js", CodeMirrorUrl + "addon/edit/closetag.js") + .SetCdnIntegrity("sha384-WIyvbwMte2q6VXgBkN7prVo9ZSmBm47nI2ftVjoJLPY4yOu9gmI6lqaNXjBwHU5k", "sha384-i4ai3UXE5wIk3ILN77PB9DhNmku+sefNKDTHXRvsrYX2bxWzm+EDmoBui5wsNU2v") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineStyle("codemirror-addon-hint-show-hint") + .SetUrl("~/OrchardCore.Resources/Styles/codemirror/addon/hint/show-hint.min.css", "~/OrchardCore.Resources/Styles/codemirror/addon/hint/show-hint.css") + .SetCdn(CodeMirrorUrl + "addon/hint/show-hint.min.css", CodeMirrorUrl + "addon/hint/show-hint.css") + .SetCdnIntegrity("sha384-qqTWkykzuDLx4yDYa7bVrwNwBHuqVvklDUMVaU4eezgNUEgGbP8Zv6i3u8OmtuWg", "sha384-ZZbLvEvLoXKrHo3Tkh7W8amMgoHFkDzWe8IAm1ZgxsG5y35H+fJCVMWwr0YBAEGA") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-hint-show-hint") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/hint/show-hint.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/hint/show-hint.js") + .SetCdn(CodeMirrorUrl + "addon/hint/show-hint.min.js", CodeMirrorUrl + "addon/hint/show-hint.js") + .SetCdnIntegrity("sha384-8i/ZCyq/QakicQDFEJhSl5oJEzzChdhEHhFBvtlxJMWdzohX6j+7O7L7NGgVjaL2", "sha384-X99dMoDW1QAOqOa49NoR3mVQPZMAmWdk1EtGVfWHdqFrD0mKdc4TVCugISfW0wEv") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-hint-sql-hint") + .SetDependencies("codemirror-addon-hint-show-hint") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/hint/sql-hint.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/hint/sql-hint.js") + .SetCdn(CodeMirrorUrl + "addon/hint/sql-hint.min.js", CodeMirrorUrl + "addon/hint/sql-hint.js") + .SetCdnIntegrity("sha384-TT6FzU/qfXKsyGpknyPBSW9YUv9boLL9TStdfYRJTMjLDdUaQwWceBVs8I326z16", "sha384-v0PZIWVaXK+SdJWDH/8f3lMh+4SXRujsh67aPj27BlUq4ocyn0Yime8qyi8AArtz") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-mode-multiplex") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/mode/multiplex.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/mode/multiplex.js") + .SetCdn(CodeMirrorUrl + "addon/mode/multiplex.min.js", CodeMirrorUrl + "addon/mode/multiplex.js") + .SetCdnIntegrity("sha384-Qr/1hjoJmzEf6ToQLo1nr8/GF5ekpaqUx0DM71QHZ4N8+c8l8aqlQ25whgApmChE", "sha384-pgj6NfGJNeIBDFqYM8m/ah3J269WhaJDiT0fN/C0c335qDMJAbfz/O2rVB53w+1D") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-mode-simple") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/mode/simple.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/mode/simple.js") + .SetCdn(CodeMirrorUrl + "addon/mode/simple.min.js", CodeMirrorUrl + "addon/mode/simple.js") + .SetCdnIntegrity("sha384-5+aYjV0V2W3IwhAYp/9WOrGMv1TaYkCjnkkW7Hv3yJQo28MergRCSRaUIUzDUs2J", "sha384-qeu+SDWpTAqXUyXdqdwbMDeYSQiv6rErEHLJ/cITY3wtuYhN2LdBSBZeS7Jyn8nv") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-addon-selection-active-line") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/addon/selection/active-line.min.js", "~/OrchardCore.Resources/Scripts/codemirror/addon/selection/active-line.js") + .SetCdn(CodeMirrorUrl + "addon/selection/active-line.min.js", CodeMirrorUrl + "addon/selection/active-line.js") + .SetCdnIntegrity("sha384-hcxaXyAtJ30s2NeDu1OHWsQRiHiWuYLTbI596+YFb+f2pFhzO0mDuahZziRPPDxg", "sha384-QhD10EHRrst6CIOzeEBXQhUT95YVJN1EV8uX2Jb5S3+qw/ozbvUE5Zn5uILnZg96") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-mode-css") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/css/css.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/css/css.js") + .SetCdn(CodeMirrorUrl + "mode/css/css.min.js", CodeMirrorUrl + "mode/css/css.js") + .SetCdnIntegrity("sha384-fpeIC2FZuPmw7mIsTvgB5BNc8QVxQC/nWg2W+CgPYOAiBiYVuHe2E8HiTWHBMIJQ", "sha384-ZD4C1ohrucZOfP7+jQSuBELICO7Z73CFD5stbjic1D3DbZk88mqj3KsRjSml/NCK") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-mode-htmlmixed") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/htmlmixed/htmlmixed.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/htmlmixed/htmlmixed.js") + .SetCdn(CodeMirrorUrl + "mode/htmlmixed/htmlmixed.min.js", CodeMirrorUrl + "mode/htmlmixed/htmlmixed.js") + .SetCdnIntegrity("sha384-xYIbc5F55vPi7pb/lUnFj3wu24HlpAMZdtBHkNrb2YhPzJV3pX7+eqXT2PXSNMrw", "sha384-0MH/N0DfWPIbCXDe9I7tmLw0dsJ4gKQUijwCcpaTgGxrTTrypsJwEOOqi5yhkQiK") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-mode-javascript") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/javascript/javascript.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/javascript/javascript.js") + .SetCdn(CodeMirrorUrl + "mode/javascript/javascript.min.js", CodeMirrorUrl + "mode/javascript/javascript.js") + .SetCdnIntegrity("sha384-kmQrbJf09Uo1WRLMDVGoVG3nM6F48frIhcj7f3FDUjeRzsiHwyBWDjMUIttnIeAf", "sha384-fgstVTG7RpZvquT2ZtQeU9iJXfvK9wTstlhasVKML77E5oe3Wi0AfLzY2grsjwmh") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-mode-sql") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/sql/sql.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/sql/sql.js") + .SetCdn(CodeMirrorUrl + "mode/sql/sql.min.js", CodeMirrorUrl + "mode/sql/sql.js") + .SetCdnIntegrity("sha384-cof/65v3Fn+7YeqmMp8aCI1xZ2yeX8JnWhQYdMzaO7VDpBOtLjfdwx6pEfUo7SFT", "sha384-Llr4/OHf89ufsfG7HbSwqB7KTd1d4hXok/Yxi72qWvLwtd6pz6nqJdOGlDdpjw6I") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineScript("codemirror-mode-xml") + .SetUrl("~/OrchardCore.Resources/Scripts/codemirror/mode/xml/xml.min.js", "~/OrchardCore.Resources/Scripts/codemirror/mode/xml/xml.js") + .SetCdn(CodeMirrorUrl + "mode/xml/xml.min.js", CodeMirrorUrl + "mode/xml/xml.js") + .SetCdnIntegrity("sha384-xPpkMo5nDgD98fIcuRVYhxkZV6/9Y4L8s3p0J5c4MxgJkyKJ8BJr+xfRkq7kn6Tw", "sha384-KX/+LdYWr3JcKfT7HK55DC3oPVJwnJSympb1qoO14sxVDtDIg+xHPVLltqJEbitI") + .SetVersion(CodeMirrorVersion); + + manifest + .DefineStyle("font-awesome") + .SetUrl("~/OrchardCore.Resources/Styles/font-awesome.min.css", "~/OrchardCore.Resources/Styles/font-awesome.css") + .SetCdn("https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css", "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css") + .SetCdnIntegrity("sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN", "sha384-FckWOBo7yuyMS7In0aXZ0aoVvnInlnFMwCv77x9sZpFgOonQgnBj1uLwenWVtsEj") + .SetVersion("4.7.0"); + + manifest + .DefineStyle("font-awesome") + .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.min.css", "~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.css") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.css") + .SetCdnIntegrity("sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm", "sha384-7rgjkhkxJ95zOzIjk97UrBOe14KgYpH9+zQm5BdgzjQELBU6kHf4WwoQzHfTx5sw") + .SetVersion("5.15.4"); + + manifest + .DefineScript("font-awesome") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/all.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/all.js") + .SetCdnIntegrity("sha384-rOA1PnstxnOBLzCLMcre8ybwbTmemjzdNlILg8O7z1lUkLXozs4DHonlDtnE7fpc", "sha384-HfU7cInvKb8zxQuLKtKr/suuRgcSH1OYsdJU+8lGA/t8nyNgdJF09UIkRzg1iefj") + .SetVersion("5.15.4"); + + manifest + .DefineScript("font-awesome-v4-shims") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/v4-shims.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/js/v4-shims.js") + .SetCdnIntegrity("sha384-bx00wqJq+zY9QLCMa/zViZPu1f0GJ3VXwF4GSw3GbfjwO28QCFr4qadCrNmJQ/9N", "sha384-SGuqaGE4bcW7Xl5T06BsUPUA91qaNtT53uGOcGpavQMje3goIFJbDsC0VAwtgL5g") + .SetVersion("5.15.4"); + + manifest + .DefineStyle("font-awesome") + .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.min.css", "~/OrchardCore.Resources/Vendor/fontawesome-free/css/all.css") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.min.css", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/css/all.css") + .SetCdnIntegrity("sha384-h/hnnw1Bi4nbpD6kE7nYfCXzovi622sY5WBxww8ARKwpdLj5kUWjRuyiXaD1U2JT", "sha384-vMdawx0r3BjxHQwcfWi0YSemtW6u5mKxTKPPh1ogUICPLaEa/6e42yg2wRYzzJtx") + .SetVersion("6.6.0"); + + manifest + .DefineScript("font-awesome") + .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/js/all.min.js", "~/OrchardCore.Resources/Vendor/fontawesome-free/js/all.js") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/all.js") + .SetCdnIntegrity("sha384-dgEl3vRKux81M373f/TdgoDTV5oZj+yjHrr/1qR5b4btG5q63kYS62t5kod+7Q6v", "sha384-v72QeVpDL6Ne4X2S+fwIXCOLhO57lwIR/qRV05SHAExDg1QoyJGWDxx6VQO3rzlC") + .SetVersion("6.6.0"); + + manifest + .DefineScript("font-awesome-v4-shims") + .SetUrl("~/OrchardCore.Resources/Vendor/fontawesome-free/js/v4-shims.min.js", "~/OrchardCore.Resources/Vendor/fontawesome-free/js/v4-shims.js") + .SetCdn("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/v4-shims.min.js", "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.6.0/js/v4-shims.js") + .SetCdnIntegrity("sha384-M9y++reQwf5nddw5loUHChCbGE4kwaeHzeEM2yWidMfaRMQeHM6MSwwPuiSnSMHF", "sha384-WrIndr6nwB5l/sVUyZkWDlpNS9Vx/Y+zhmjZNP+j2UJOBmy36ufaIIqd7o2kL0BL") + .SetVersion("6.6.0"); + + manifest + .DefineScript("jquery-resizable") + .SetDependencies("jQuery") + .SetUrl("~/OrchardCore.Resources/Scripts/jquery-resizable.min.js", "~/OrchardCore.Resources/Scripts/jquery-resizable.js") + .SetCdn("https://cdn.jsdelivr.net/npm/jquery-resizable-dom@0.35.0/dist/jquery-resizable.min.js") + .SetCdnIntegrity("sha384-1LMjDEezsSgzlRgsyFIAvLW7FWSdFIHqBGjUa+ad5EqtK1FORC8XpTJ/pahxj5GB", "sha384-0yk9X0IG0cXxuN9yTTkps/3TNNI9ZcaKKhh8dgqOEAWGXxIYS5xaY2as6b32Ov3P") + .SetVersion("0.35.0"); + + manifest + .DefineStyle("trumbowyg") + .SetUrl("~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg.min.css", "~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg.css") + .SetCdn("https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/ui/trumbowyg.min.css", "https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/ui/trumbowyg.css") + .SetCdnIntegrity("sha384-XfI6P0jtm0X3QDEQxS1DotzhIXkJeuSV1wtBOntPaRxzpzTkMWkpZpkuzj8qVHzl", "sha384-GUyfWhYsIKKkAMejJuy50VTSfyfkrrJX2csg7fxJJt7vi+gXH8qxqH29C5GURaum") + .SetVersion("2.28.0"); + + manifest + .DefineScript("trumbowyg") + .SetDependencies("jquery-resizable") + .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.js") + .SetCdn("https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/trumbowyg.min.js", "https://cdn.jsdelivr.net/npm/trumbowyg@2.28.0/dist/trumbowyg.js") + .SetCdnIntegrity("sha384-O4OAGMDq5hnqt4/WQz+fW6yVgZ02jmw+Yf1j02zIgglnCYXf/7TmET8tFbrTN6u5", "sha384-dNxlebCuuiNWPhPBEd69nEAtkEWa7Z9IWkrL+OSmJ456dlu6TAASXgL72Bn4GGju") + .SetVersion("2.28.0"); + + manifest + .DefineScript("trumbowyg-shortcodes") + .SetDependencies("trumbowyg") + .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.shortcodes.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.shortcodes.js") + .SetVersion("1.0.0"); + + manifest + .DefineScript("trumbowyg-theme") + .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.theme.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg.theme.js") + .SetDependencies("trumbowyg", "theme-head") + .SetVersion("1.0.0"); + + manifest + .DefineStyle("trumbowyg-plugins") + .SetDependencies("trumbowyg") + .SetUrl("~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg-plugins.min.css", "~/OrchardCore.Resources/Styles/trumbowyg/trumbowyg-plugins.css") + .SetVersion("2.28.0"); + + manifest + .DefineScript("trumbowyg-plugins") + .SetDependencies("trumbowyg") + .SetUrl("~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg-plugins.min.js", "~/OrchardCore.Resources/Scripts/trumbowyg/trumbowyg-plugins.js") + .SetVersion("2.28.0"); + + manifest + .DefineScript("credential-helpers") + .SetUrl("~/OrchardCore.Resources/Scripts/credential-helpers.min.js", "~/OrchardCore.Resources/Scripts/credential-helpers.js") + .SetVersion("1.0.0"); + + manifest + .DefineScript("vuejs") + .SetUrl("~/OrchardCore.Resources/Scripts/vue.min.js", "~/OrchardCore.Resources/Scripts/vue.js") + .SetCdn("https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js", "https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js") + .SetCdnIntegrity("sha384-ULpZhk1pvhc/UK5ktA9kwb2guy9ovNSTyxPNHANnA35YjBQgdwI+AhLkixDvdlw4", "sha384-t1tHLsbM7bYMJCXlhr0//00jSs7ZhsAhxgm191xFsyzvieTMCbUWKMhFg9I6ci8q") + .SetVersion("2.6.14"); + + manifest + .DefineScript("vue-multiselect") + .SetDependencies("vuejs") + .SetUrl("~/OrchardCore.Resources/Scripts/vue-multiselect.min.js", "~/OrchardCore.Resources/Scripts/vue-multiselect.min.js") + .SetCdn("https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.js", "https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.js") + .SetCdnIntegrity("sha384-a4eXewRTYCwYdFtSnMCZTNtiXrfdul6aQdueRgHPAx2y1Ldp0QaFdCTpOx0ycsXU", "sha384-a4eXewRTYCwYdFtSnMCZTNtiXrfdul6aQdueRgHPAx2y1Ldp0QaFdCTpOx0ycsXU") + .SetVersion("2.1.6"); + + manifest + .DefineStyle("vue-multiselect") + .SetUrl("~/OrchardCore.Resources/Styles/vue-multiselect.min.css", "~/OrchardCore.Resources/Styles/vue-multiselect.min.css") + .SetCdn("https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.css", "https://cdn.jsdelivr.net/npm/vue-multiselect@2.1.6/dist/vue-multiselect.min.css") + .SetCdnIntegrity("sha384-PPH/T7V86Z1+B4eMPef4FJXLD5fsTpObWoCoK3CiNtSX7aji+5qxpOCn1f2TDYAM", "sha384-PPH/T7V86Z1+B4eMPef4FJXLD5fsTpObWoCoK3CiNtSX7aji+5qxpOCn1f2TDYAM") + .SetVersion("2.1.6"); + + manifest + .DefineScript("Sortable") + .SetUrl("~/OrchardCore.Resources/Scripts/Sortable.min.js", "~/OrchardCore.Resources/Scripts/Sortable.js") + .SetCdn("https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js", "https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.js") + .SetCdnIntegrity("sha384-eeLEhtwdMwD3X9y+8P3Cn7Idl/M+w8H4uZqkgD/2eJVkWIN1yKzEj6XegJ9dL3q0", "sha384-OFIl93h6jYoAF+hATXncsLYMiLo81FpuReH3fgCI4wep7qNCmiA2I0bwcvVqHSBj") + .SetVersion("1.15.0"); + + manifest + .DefineScript("vuedraggable") + .SetDependencies("vuejs", "Sortable") + .SetUrl("~/OrchardCore.Resources/Scripts/vuedraggable.umd.min.js", "~/OrchardCore.Resources/Scripts/vuedraggable.umd.js") + .SetCdn("https://cdn.jsdelivr.net/npm/vuedraggable@2.24.3/dist/vuedraggable.umd.min.js", "https://cdn.jsdelivr.net/npm/vuedraggable@2.24.3/dist/vuedraggable.umd.js") + .SetCdnIntegrity("sha384-qUA1xXJiX23E4GOeW/XHtsBkV9MUcHLSjhi3FzO08mv8+W8bv5AQ1cwqLskycOTs", "sha384-+jB9vXc/EaIJTlNiZG2tv+TUpKm6GR9HCRZb3VkI3lscZWqrCYDbX2ZXffNJldL9") + .SetVersion("2.24.3"); + + manifest + .DefineScript("js-cookie") + .SetUrl("~/OrchardCore.Resources/Scripts/js.cookie.min.js", "~/OrchardCore.Resources/Scripts/js.cookie.js") + .SetCdn("https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js", "https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.js") + .SetCdnIntegrity("sha384-/vxhYfM1LENRhdpZ8dwEsQn/X4VhpbEZSiU4m/FwR+PVpzar4fkEOw8FP9Y+OfQN", "sha384-b1TD0tFP+Ao4jmFaQw9RQxezUooFrLdlqfDfoh1SKv5L3jG7dD44QiwD+UzckH8W") + .SetVersion("3.0.5"); + + manifest + .DefineScript("monaco-loader") + .SetUrl("~/OrchardCore.Resources/Scripts/monaco/vs/loader.js") + .SetPosition(ResourcePosition.Last) + .SetVersion(MonacoEditorVersion); + + manifest + .DefineScript("monaco") + .SetAttribute("data-tenant-prefix", _pathBase) + .SetUrl("~/OrchardCore.Resources/Scripts/monaco/ocmonaco.js") + .SetDependencies("monaco-loader") + .SetVersion(MonacoEditorVersion); + + return manifest; + } + + public void Configure(ResourceManagementOptions options) + { + options.ResourceManifests.Add(BuildManifest()); - public void Configure(ResourceManagementOptions options) + switch (_resourceOptions.ResourceDebugMode) { - options.ResourceManifests.Add(BuildManifest()); - - switch (_resourceOptions.ResourceDebugMode) - { - case ResourceDebugMode.Enabled: - options.DebugMode = true; - break; - - case ResourceDebugMode.Disabled: - options.DebugMode = false; - break; - - case ResourceDebugMode.FromConfiguration: - options.DebugMode = !_env.IsProduction(); - break; - } - - options.UseCdn = _resourceOptions.UseCdn; - options.CdnBaseUrl = _resourceOptions.CdnBaseUrl; - options.AppendVersion = _resourceOptions.AppendVersion; - options.ContentBasePath = _pathBase.Value; + case ResourceDebugMode.Enabled: + options.DebugMode = true; + break; + + case ResourceDebugMode.Disabled: + options.DebugMode = false; + break; + + case ResourceDebugMode.FromConfiguration: + options.DebugMode = !_env.IsProduction(); + break; } + + options.UseCdn = _resourceOptions.UseCdn; + options.CdnBaseUrl = _resourceOptions.CdnBaseUrl; + options.AppendVersion = _resourceOptions.AppendVersion; + options.ContentBasePath = _pathBase.Value; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs index cb6cdb4e6d4..85dfe85272b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs @@ -7,36 +7,35 @@ using OrchardCore.Resources.Liquid; using OrchardCore.Resources.Services; -namespace OrchardCore.Resources +namespace OrchardCore.Resources; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - private readonly IShellConfiguration _shellConfiguration; + private readonly IShellConfiguration _shellConfiguration; - public Startup(IShellConfiguration shellConfiguration) - { - _shellConfiguration = shellConfiguration; - } + public Startup(IShellConfiguration shellConfiguration) + { + _shellConfiguration = shellConfiguration; + } - public override void ConfigureServices(IServiceCollection serviceCollection) + public override void ConfigureServices(IServiceCollection serviceCollection) + { + serviceCollection.Configure(o => { - serviceCollection.Configure(o => - { - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("meta", parser.ArgumentsListParser, MetaTag.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("link", parser.ArgumentsListParser, LinkTag.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("script", parser.ArgumentsListParser, ScriptTag.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("style", parser.ArgumentsListParser, StyleTag.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("resources", parser.ArgumentsListParser, ResourcesTag.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserBlock("scriptblock", parser.ArgumentsListParser, ScriptBlock.WriteToAsync)); - o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserBlock("styleblock", parser.ArgumentsListParser, StyleBlock.WriteToAsync)); - }); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("meta", parser.ArgumentsListParser, MetaTag.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("link", parser.ArgumentsListParser, LinkTag.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("script", parser.ArgumentsListParser, ScriptTag.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("style", parser.ArgumentsListParser, StyleTag.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("resources", parser.ArgumentsListParser, ResourcesTag.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserBlock("scriptblock", parser.ArgumentsListParser, ScriptBlock.WriteToAsync)); + o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserBlock("styleblock", parser.ArgumentsListParser, StyleBlock.WriteToAsync)); + }); - serviceCollection.AddTransient, ResourceManagementOptionsConfiguration>(); + serviceCollection.AddTransient, ResourceManagementOptionsConfiguration>(); - var resourceConfiguration = _shellConfiguration.GetSection("OrchardCore_Resources"); - serviceCollection.Configure(resourceConfiguration); + var resourceConfiguration = _shellConfiguration.GetSection("OrchardCore_Resources"); + serviceCollection.Configure(resourceConfiguration); - serviceCollection.AddScoped(); - } + serviceCollection.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ResponseCompression/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ResponseCompression/Startup.cs index 0b03e28a9fc..d8a37564d32 100644 --- a/src/OrchardCore.Modules/OrchardCore.ResponseCompression/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ResponseCompression/Startup.cs @@ -4,20 +4,19 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.Modules; -namespace OrchardCore.ResponseCompression +namespace OrchardCore.ResponseCompression; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - public override int Order => -5; + public override int Order => -5; - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseResponseCompression(); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseResponseCompression(); + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddResponseCompression(options => options.EnableForHttps = true); - } + public override void ConfigureServices(IServiceCollection services) + { + services.AddResponseCompression(options => options.EnableForHttps = true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/AdminMenu.cs index ff9ba235152..d15bcdd1dd8 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/AdminMenu.cs @@ -4,44 +4,43 @@ using OrchardCore.Navigation; using OrchardCore.ReverseProxy.Drivers; -namespace OrchardCore.ReverseProxy +namespace OrchardCore.ReverseProxy; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", ReverseProxySettingsDisplayDriver.GroupId}, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", ReverseProxySettingsDisplayDriver.GroupId}, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], settings => settings - .Add(S["Reverse Proxy"], S["Reverse Proxy"].PrefixPosition(), entry => entry - .AddClass("reverseproxy") - .Id("reverseproxy") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageReverseProxySettings) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], settings => settings + .Add(S["Reverse Proxy"], S["Reverse Proxy"].PrefixPosition(), entry => entry + .AddClass("reverseproxy") + .Id("reverseproxy") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageReverseProxySettings) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Drivers/ReverseProxySettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Drivers/ReverseProxySettingsDisplayDriver.cs index f092529334e..96c262cfa83 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Drivers/ReverseProxySettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Drivers/ReverseProxySettingsDisplayDriver.cs @@ -10,82 +10,81 @@ using OrchardCore.ReverseProxy.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.ReverseProxy.Drivers +namespace OrchardCore.ReverseProxy.Drivers; + +public sealed class ReverseProxySettingsDisplayDriver : SiteDisplayDriver { - public sealed class ReverseProxySettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "ReverseProxy"; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ReverseProxySettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - public const string GroupId = "ReverseProxy"; + _shellReleaseManager = shellReleaseManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } + + protected override string SettingsGroupId + => GroupId; - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + public override async Task EditAsync(ISite site, ReverseProxySettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public ReverseProxySettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings)) { - _shellReleaseManager = shellReleaseManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => GroupId; + context.AddTenantReloadWarningWrapper(); - public override async Task EditAsync(ISite site, ReverseProxySettings settings, BuildEditorContext context) + return Initialize("ReverseProxySettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); - - return Initialize("ReverseProxySettings_Edit", model => - { - model.EnableXForwardedFor = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor); - model.EnableXForwardedHost = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost); - model.EnableXForwardedProto = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto); - }).Location("Content:2") - .OnGroup(SettingsGroupId); - } + model.EnableXForwardedFor = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor); + model.EnableXForwardedHost = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost); + model.EnableXForwardedProto = settings.ForwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto); + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - public override async Task UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; + public override async Task UpdateAsync(ISite site, ReverseProxySettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings)) - { - return null; - } + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageReverseProxySettings)) + { + return null; + } - var model = new ReverseProxySettingsViewModel(); + var model = new ReverseProxySettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - settings.ForwardedHeaders = ForwardedHeaders.None; + settings.ForwardedHeaders = ForwardedHeaders.None; - if (model.EnableXForwardedFor) - { - settings.ForwardedHeaders |= ForwardedHeaders.XForwardedFor; - } + if (model.EnableXForwardedFor) + { + settings.ForwardedHeaders |= ForwardedHeaders.XForwardedFor; + } - if (model.EnableXForwardedHost) - { - settings.ForwardedHeaders |= ForwardedHeaders.XForwardedHost; - } + if (model.EnableXForwardedHost) + { + settings.ForwardedHeaders |= ForwardedHeaders.XForwardedHost; + } - if (model.EnableXForwardedProto) - { - settings.ForwardedHeaders |= ForwardedHeaders.XForwardedProto; - } + if (model.EnableXForwardedProto) + { + settings.ForwardedHeaders |= ForwardedHeaders.XForwardedProto; + } - _shellReleaseManager.RequestRelease(); + _shellReleaseManager.RequestRelease(); - return await EditAsync(site, settings, context); - } + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Extensions/OrchardCoreBuilderExtensions.cs index bfa6e78e732..fd3665f6768 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,20 +2,19 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.ReverseProxy.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureReverseProxySettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureReverseProxySettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_ReverseProxy"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_ReverseProxy"); - tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); - }); + tenantServices.PostConfigure(settings => configurationSection.Bind(settings)); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ForwardedHeadersOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ForwardedHeadersOptionsConfiguration.cs index d79a0ebeead..abcfa690dba 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ForwardedHeadersOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ForwardedHeadersOptionsConfiguration.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Options; -namespace OrchardCore.ReverseProxy.Services +namespace OrchardCore.ReverseProxy.Services; + +public sealed class ForwardedHeadersOptionsConfiguration : IConfigureOptions { - public sealed class ForwardedHeadersOptionsConfiguration : IConfigureOptions - { - private readonly ReverseProxyService _reverseProxyService; + private readonly ReverseProxyService _reverseProxyService; - public ForwardedHeadersOptionsConfiguration(ReverseProxyService reverseProxyService) - { - _reverseProxyService = reverseProxyService; - } + public ForwardedHeadersOptionsConfiguration(ReverseProxyService reverseProxyService) + { + _reverseProxyService = reverseProxyService; + } - public void Configure(ForwardedHeadersOptions options) - { - var reverseProxySettings = _reverseProxyService.GetSettingsAsync().GetAwaiter().GetResult(); - options.ForwardedHeaders = reverseProxySettings.ForwardedHeaders; + public void Configure(ForwardedHeadersOptions options) + { + var reverseProxySettings = _reverseProxyService.GetSettingsAsync().GetAwaiter().GetResult(); + options.ForwardedHeaders = reverseProxySettings.ForwardedHeaders; - // later we can add known networks and know proxies, for now we accept all - options.KnownNetworks.Clear(); - options.KnownProxies.Clear(); - } + // later we can add known networks and know proxies, for now we accept all + options.KnownNetworks.Clear(); + options.KnownProxies.Clear(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ReverseProxyService.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ReverseProxyService.cs index 69e16baa3ca..fbd73e94cef 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ReverseProxyService.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Services/ReverseProxyService.cs @@ -2,18 +2,17 @@ using OrchardCore.ReverseProxy.Settings; using OrchardCore.Settings; -namespace OrchardCore.ReverseProxy.Services -{ - public class ReverseProxyService - { - private readonly ISiteService _siteService; +namespace OrchardCore.ReverseProxy.Services; - public ReverseProxyService(ISiteService siteService) - { - _siteService = siteService; - } +public class ReverseProxyService +{ + private readonly ISiteService _siteService; - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public ReverseProxyService(ISiteService siteService) + { + _siteService = siteService; } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Settings/ReverseProxySettings.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Settings/ReverseProxySettings.cs index 82593ede6bc..f4e1097d921 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Settings/ReverseProxySettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Settings/ReverseProxySettings.cs @@ -1,9 +1,8 @@ using Microsoft.AspNetCore.HttpOverrides; -namespace OrchardCore.ReverseProxy.Settings +namespace OrchardCore.ReverseProxy.Settings; + +public class ReverseProxySettings { - public class ReverseProxySettings - { - public ForwardedHeaders ForwardedHeaders { get; set; } - } + public ForwardedHeaders ForwardedHeaders { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Startup.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Startup.cs index f709971fd15..bf61dd99d23 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/Startup.cs @@ -14,37 +14,36 @@ using OrchardCore.Settings; using OrchardCore.Settings.Deployment; -namespace OrchardCore.ReverseProxy +namespace OrchardCore.ReverseProxy; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase - { - public override int Order - => OrchardCoreConstants.ConfigureOrder.ReverseProxy; + public override int Order + => OrchardCoreConstants.ConfigureOrder.ReverseProxy; - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseForwardedHeaders(); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseForwardedHeaders(); + } - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped, ReverseProxySettingsDisplayDriver>(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped, ReverseProxySettingsDisplayDriver>(); - services.AddSingleton(); + services.AddSingleton(); - services.TryAddEnumerable(ServiceDescriptor - .Transient, ForwardedHeadersOptionsConfiguration>()); - } + services.TryAddEnumerable(ServiceDescriptor + .Transient, ForwardedHeadersOptionsConfiguration>()); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Reverse Proxy settings"], S => S["Exports the Reverse Proxy settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Reverse Proxy settings"], S => S["Exports the Reverse Proxy settings."]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/ViewModels/ReverseProxySettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/ViewModels/ReverseProxySettingsViewModel.cs index bb7c93fc822..a578523f882 100644 --- a/src/OrchardCore.Modules/OrchardCore.ReverseProxy/ViewModels/ReverseProxySettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.ReverseProxy/ViewModels/ReverseProxySettingsViewModel.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.ReverseProxy.ViewModels +namespace OrchardCore.ReverseProxy.ViewModels; + +public class ReverseProxySettingsViewModel { - public class ReverseProxySettingsViewModel - { - public bool EnableXForwardedFor { get; set; } + public bool EnableXForwardedFor { get; set; } - public bool EnableXForwardedProto { get; set; } + public bool EnableXForwardedProto { get; set; } - public bool EnableXForwardedHost { get; set; } - } + public bool EnableXForwardedHost { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Roles/AdminMenu.cs index 3a884cc4729..bed37dd4b12 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/AdminMenu.cs @@ -2,35 +2,34 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Roles +namespace OrchardCore.Roles; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Security"], security => security - .Add(S["Roles"], S["Roles"].PrefixPosition(), roles => roles - .AddClass("roles").Id("roles") - .Action("Index", "Admin", "OrchardCore.Roles") - .Permission(CommonPermissions.ManageRoles) - .LocalNav() - ) - ); + builder + .Add(S["Security"], security => security + .Add(S["Roles"], S["Roles"].PrefixPosition(), roles => roles + .AddClass("roles").Id("roles") + .Action("Index", "Admin", "OrchardCore.Roles") + .Permission(CommonPermissions.ManageRoles) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs index 2eec27f675d..cccc10ca69a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Controllers/AdminController.cs @@ -19,300 +19,299 @@ using OrchardCore.Security.Permissions; using OrchardCore.Security.Services; -namespace OrchardCore.Roles.Controllers +namespace OrchardCore.Roles.Controllers; + +[Admin("Roles/{action}/{id?}", "Roles{action}")] +public class AdminController : Controller { - [Admin("Roles/{action}/{id?}", "Roles{action}")] - public class AdminController : Controller + private readonly IDocumentStore _documentStore; + private readonly RoleManager _roleManager; + private readonly IAuthorizationService _authorizationService; + private readonly IEnumerable _permissionProviders; + private readonly ITypeFeatureProvider _typeFeatureProvider; + private readonly IShellFeaturesManager _shellFeaturesManager; + private readonly IRoleService _roleService; + private readonly INotifier _notifier; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + IDocumentStore documentStore, + RoleManager roleManager, + IAuthorizationService authorizationService, + IEnumerable permissionProviders, + ITypeFeatureProvider typeFeatureProvider, + IShellFeaturesManager shellFeaturesManager, + IRoleService roleService, + INotifier notifier, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) + { + _documentStore = documentStore; + _roleManager = roleManager; + _authorizationService = authorizationService; + _permissionProviders = permissionProviders; + _typeFeatureProvider = typeFeatureProvider; + _shellFeaturesManager = shellFeaturesManager; + _roleService = roleService; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + } + + public async Task Index() { - private readonly IDocumentStore _documentStore; - private readonly RoleManager _roleManager; - private readonly IAuthorizationService _authorizationService; - private readonly IEnumerable _permissionProviders; - private readonly ITypeFeatureProvider _typeFeatureProvider; - private readonly IShellFeaturesManager _shellFeaturesManager; - private readonly IRoleService _roleService; - private readonly INotifier _notifier; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - IDocumentStore documentStore, - RoleManager roleManager, - IAuthorizationService authorizationService, - IEnumerable permissionProviders, - ITypeFeatureProvider typeFeatureProvider, - IShellFeaturesManager shellFeaturesManager, - IRoleService roleService, - INotifier notifier, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) { - _documentStore = documentStore; - _roleManager = roleManager; - _authorizationService = authorizationService; - _permissionProviders = permissionProviders; - _typeFeatureProvider = typeFeatureProvider; - _shellFeaturesManager = shellFeaturesManager; - _roleService = roleService; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - public async Task Index() - { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) - { - return Forbid(); - } + var roles = await _roleService.GetRolesAsync(); - var roles = await _roleService.GetRolesAsync(); + var model = new RolesViewModel + { + RoleEntries = roles.Select(BuildRoleEntry).ToList() + }; - var model = new RolesViewModel - { - RoleEntries = roles.Select(BuildRoleEntry).ToList() - }; + return View(model); + } - return View(model); + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + { + return Forbid(); } - public async Task Create() - { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) - { - return Forbid(); - } + var model = new CreateRoleViewModel(); - var model = new CreateRoleViewModel(); + return View(model); + } - return View(model); + [HttpPost] + public async Task Create(CreateRoleViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + { + return Forbid(); } - [HttpPost] - public async Task Create(CreateRoleViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) - { - return Forbid(); - } + model.RoleName = model.RoleName.Trim(); - if (ModelState.IsValid) + if (model.RoleName.Contains('/')) { - model.RoleName = model.RoleName.Trim(); - - if (model.RoleName.Contains('/')) - { - ModelState.AddModelError(string.Empty, S["Invalid role name."]); - } - - if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(model.RoleName)) != null) - { - ModelState.AddModelError(string.Empty, S["The role is already used."]); - } + ModelState.AddModelError(string.Empty, S["Invalid role name."]); } - if (ModelState.IsValid) + if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(model.RoleName)) != null) { - var role = new Role { RoleName = model.RoleName, RoleDescription = model.RoleDescription }; - var result = await _roleManager.CreateAsync(role); - if (result.Succeeded) - { - await _notifier.SuccessAsync(H["Role created successfully."]); - return RedirectToAction(nameof(Index)); - } - - await _documentStore.CancelAsync(); - - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } + ModelState.AddModelError(string.Empty, S["The role is already used."]); } - - // If we got this far, something failed, redisplay form - return View(model); } - [HttpPost] - public async Task Delete(string id) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + var role = new Role { RoleName = model.RoleName, RoleDescription = model.RoleDescription }; + var result = await _roleManager.CreateAsync(role); + if (result.Succeeded) { - return Forbid(); + await _notifier.SuccessAsync(H["Role created successfully."]); + return RedirectToAction(nameof(Index)); } - var currentRole = await _roleManager.FindByIdAsync(id); + await _documentStore.CancelAsync(); - if (currentRole == null) + foreach (var error in result.Errors) { - return NotFound(); + ModelState.AddModelError(string.Empty, error.Description); } + } - var result = await _roleManager.DeleteAsync(currentRole); - - if (result.Succeeded) - { - await _notifier.SuccessAsync(H["Role deleted successfully."]); - } - else - { - await _documentStore.CancelAsync(); + // If we got this far, something failed, redisplay form + return View(model); + } - await _notifier.ErrorAsync(H["Could not delete this role."]); + [HttpPost] + public async Task Delete(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + { + return Forbid(); + } - foreach (var error in result.Errors) - { - await _notifier.ErrorAsync(H[error.Description]); - } - } + var currentRole = await _roleManager.FindByIdAsync(id); - return RedirectToAction(nameof(Index)); + if (currentRole == null) + { + return NotFound(); } - public async Task Edit(string id) + var result = await _roleManager.DeleteAsync(currentRole); + + if (result.Succeeded) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) - { - return Forbid(); - } + await _notifier.SuccessAsync(H["Role deleted successfully."]); + } + else + { + await _documentStore.CancelAsync(); + + await _notifier.ErrorAsync(H["Could not delete this role."]); - if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(id)) is not Role role) + foreach (var error in result.Errors) { - return NotFound(); + await _notifier.ErrorAsync(H[error.Description]); } + } - var installedPermissions = await GetInstalledPermissionsAsync(); - var allPermissions = installedPermissions.SelectMany(x => x.Value); + return RedirectToAction(nameof(Index)); + } - var model = new EditRoleViewModel - { - Role = role, - Name = role.RoleName, - RoleDescription = role.RoleDescription, - EffectivePermissions = await GetEffectivePermissions(role, allPermissions), - RoleCategoryPermissions = installedPermissions - }; - - return View(model); + public async Task Edit(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Edit))] - public async Task EditPost(string id, string roleDescription) + if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(id)) is not Role role) { - if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) - { - return Forbid(); - } + return NotFound(); + } - if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(id)) is not Role role) - { - return NotFound(); - } + var installedPermissions = await GetInstalledPermissionsAsync(); + var allPermissions = installedPermissions.SelectMany(x => x.Value); - role.RoleDescription = roleDescription; + var model = new EditRoleViewModel + { + Role = role, + Name = role.RoleName, + RoleDescription = role.RoleDescription, + EffectivePermissions = await GetEffectivePermissions(role, allPermissions), + RoleCategoryPermissions = installedPermissions + }; + + return View(model); + } - // Save. - var rolePermissions = new List(); - foreach (var key in Request.Form.Keys) + [HttpPost, ActionName(nameof(Edit))] + public async Task EditPost(string id, string roleDescription) + { + if (!await _authorizationService.AuthorizeAsync(User, CommonPermissions.ManageRoles)) + { + return Forbid(); + } + + if (await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(id)) is not Role role) + { + return NotFound(); + } + + role.RoleDescription = roleDescription; + + // Save. + var rolePermissions = new List(); + foreach (var key in Request.Form.Keys) + { + if (key.StartsWith("Checkbox.", StringComparison.Ordinal) && Request.Form[key] == "true") { - if (key.StartsWith("Checkbox.", StringComparison.Ordinal) && Request.Form[key] == "true") - { - var permissionName = key["Checkbox.".Length..]; - rolePermissions.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permissionName }); - } + var permissionName = key["Checkbox.".Length..]; + rolePermissions.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permissionName }); } + } - role.RoleClaims.RemoveAll(c => c.ClaimType == Permission.ClaimType); - role.RoleClaims.AddRange(rolePermissions); + role.RoleClaims.RemoveAll(c => c.ClaimType == Permission.ClaimType); + role.RoleClaims.AddRange(rolePermissions); - await _roleManager.UpdateAsync(role); + await _roleManager.UpdateAsync(role); - await _notifier.SuccessAsync(H["Role updated successfully."]); + await _notifier.SuccessAsync(H["Role updated successfully."]); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); + } - private RoleEntry BuildRoleEntry(IRole role) + private RoleEntry BuildRoleEntry(IRole role) + { + return new RoleEntry { - return new RoleEntry - { - Name = role.RoleName, - Description = role.RoleDescription, - Selected = false - }; - } + Name = role.RoleName, + Description = role.RoleDescription, + Selected = false + }; + } + + private async Task>> GetInstalledPermissionsAsync() + { + var installedPermissions = new Dictionary>(); + var enabledFeatures = await _shellFeaturesManager.GetEnabledFeaturesAsync(); - private async Task>> GetInstalledPermissionsAsync() + foreach (var permissionProvider in _permissionProviders) { - var installedPermissions = new Dictionary>(); - var enabledFeatures = await _shellFeaturesManager.GetEnabledFeaturesAsync(); + // Two features could use the same permission. + var feature = _typeFeatureProvider + .GetFeaturesForDependency(permissionProvider.GetType()) + .LastOrDefault(feature => enabledFeatures.Any(enabledFeature => feature.Id == enabledFeature.Id)); - foreach (var permissionProvider in _permissionProviders) - { - // Two features could use the same permission. - var feature = _typeFeatureProvider - .GetFeaturesForDependency(permissionProvider.GetType()) - .LastOrDefault(feature => enabledFeatures.Any(enabledFeature => feature.Id == enabledFeature.Id)); + var permissions = await permissionProvider.GetPermissionsAsync(); - var permissions = await permissionProvider.GetPermissionsAsync(); + foreach (var permission in permissions) + { + var groupKey = GetGroupKey(feature, permission.Category); - foreach (var permission in permissions) + if (installedPermissions.TryGetValue(groupKey, out var value)) { - var groupKey = GetGroupKey(feature, permission.Category); - - if (installedPermissions.TryGetValue(groupKey, out var value)) - { - installedPermissions[groupKey] = value.Concat(new[] { permission }); + installedPermissions[groupKey] = value.Concat(new[] { permission }); - continue; - } - - installedPermissions.Add(groupKey, new[] { permission }); + continue; } - } - - return installedPermissions; - } - private PermissionGroupKey GetGroupKey(IFeatureInfo feature, string category) - { - if (!string.IsNullOrWhiteSpace(category)) - { - return new PermissionGroupKey(category, category); + installedPermissions.Add(groupKey, new[] { permission }); } + } - var title = string.IsNullOrWhiteSpace(feature.Name) ? S["{0} Feature", feature.Id] : feature.Name; + return installedPermissions; + } - return new PermissionGroupKey(feature.Id, title) - { - Source = feature.Id, - }; + private PermissionGroupKey GetGroupKey(IFeatureInfo feature, string category) + { + if (!string.IsNullOrWhiteSpace(category)) + { + return new PermissionGroupKey(category, category); } - private async Task> GetEffectivePermissions(Role role, IEnumerable allPermissions) + var title = string.IsNullOrWhiteSpace(feature.Name) ? S["{0} Feature", feature.Id] : feature.Name; + + return new PermissionGroupKey(feature.Id, title) { - // Create a fake user to check the actual permissions. If the role is anonymous - // IsAuthenticated needs to be false. - var fakeIdentity = new ClaimsIdentity([new Claim(ClaimTypes.Role, role.RoleName)], - !string.Equals(role.RoleName, OrchardCoreConstants.Roles.Anonymous, StringComparison.OrdinalIgnoreCase) ? "FakeAuthenticationType" : null); + Source = feature.Id, + }; + } - // Add role claims - fakeIdentity.AddClaims(role.RoleClaims.Select(c => c.ToClaim())); + private async Task> GetEffectivePermissions(Role role, IEnumerable allPermissions) + { + // Create a fake user to check the actual permissions. If the role is anonymous + // IsAuthenticated needs to be false. + var fakeIdentity = new ClaimsIdentity([new Claim(ClaimTypes.Role, role.RoleName)], + !string.Equals(role.RoleName, OrchardCoreConstants.Roles.Anonymous, StringComparison.OrdinalIgnoreCase) ? "FakeAuthenticationType" : null); - var fakePrincipal = new ClaimsPrincipal(fakeIdentity); + // Add role claims + fakeIdentity.AddClaims(role.RoleClaims.Select(c => c.ToClaim())); - var result = new List(); + var fakePrincipal = new ClaimsPrincipal(fakeIdentity); - foreach (var permission in allPermissions) + var result = new List(); + + foreach (var permission in allPermissions) + { + if (await _authorizationService.AuthorizeAsync(fakePrincipal, permission)) { - if (await _authorizationService.AuthorizeAsync(fakePrincipal, permission)) - { - result.Add(permission.Name); - } + result.Add(permission.Name); } - - return result; } + + return result; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentSource.cs index 49a769415fa..b2ca3281b07 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentSource.cs @@ -9,56 +9,55 @@ using OrchardCore.Security.Permissions; using OrchardCore.Security.Services; -namespace OrchardCore.Roles.Deployment +namespace OrchardCore.Roles.Deployment; + +public class AllRolesDeploymentSource : IDeploymentSource { - public class AllRolesDeploymentSource : IDeploymentSource + private readonly RoleManager _roleManager; + private readonly IRoleService _roleService; + + public AllRolesDeploymentSource( + RoleManager roleManager, + IRoleService roleService) + { + _roleManager = roleManager; + _roleService = roleService; + } + + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - private readonly RoleManager _roleManager; - private readonly IRoleService _roleService; + var allRolesStep = step as AllRolesDeploymentStep; - public AllRolesDeploymentSource( - RoleManager roleManager, - IRoleService roleService) + if (allRolesStep == null) { - _roleManager = roleManager; - _roleService = roleService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - var allRolesStep = step as AllRolesDeploymentStep; + // Get all roles + var allRoles = await _roleService.GetRolesAsync(); + var permissions = new JsonArray(); + var tasks = new List(); - if (allRolesStep == null) - { - return; - } - - // Get all roles - var allRoles = await _roleService.GetRolesAsync(); - var permissions = new JsonArray(); - var tasks = new List(); + foreach (var role in allRoles) + { + var currentRole = (Role)await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(role.RoleName)); - foreach (var role in allRoles) + if (currentRole != null) { - var currentRole = (Role)await _roleManager.FindByNameAsync(_roleManager.NormalizeKey(role.RoleName)); - - if (currentRole != null) - { - permissions.Add(JObject.FromObject( - new RolesStepRoleModel - { - Name = currentRole.RoleName, - Description = currentRole.RoleDescription, - Permissions = currentRole.RoleClaims.Where(x => x.ClaimType == Permission.ClaimType).Select(x => x.ClaimValue).ToArray() - })); - } + permissions.Add(JObject.FromObject( + new RolesStepRoleModel + { + Name = currentRole.RoleName, + Description = currentRole.RoleDescription, + Permissions = currentRole.RoleClaims.Where(x => x.ClaimType == Permission.ClaimType).Select(x => x.ClaimValue).ToArray() + })); } - - result.Steps.Add(new JsonObject - { - ["name"] = "Roles", - ["Roles"] = permissions, - }); } + + result.Steps.Add(new JsonObject + { + ["name"] = "Roles", + ["Roles"] = permissions, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStep.cs index f2a140a5296..d1db4a0c039 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Roles.Deployment +namespace OrchardCore.Roles.Deployment; + +/// +/// Adds roles to a . +/// +public class AllRolesDeploymentStep : DeploymentStep { - /// - /// Adds roles to a . - /// - public class AllRolesDeploymentStep : DeploymentStep + public AllRolesDeploymentStep() { - public AllRolesDeploymentStep() - { - Name = "AllRoles"; - } + Name = "AllRoles"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStepDriver.cs index d09e3ec2430..c5e07acf8ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Deployment/AllRolesDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Roles.Deployment +namespace OrchardCore.Roles.Deployment; + +public sealed class AllRolesDeploymentStepDriver : DisplayDriver { - public sealed class AllRolesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllRolesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllRolesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllRolesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllRolesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllRolesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllRolesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllRolesDeploymentStep step, BuildEditorContext context) - { - return View("AllRolesDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllRolesDeploymentStep step, BuildEditorContext context) + { + return View("AllRolesDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs index 286b853cb6f..6fe8229f57a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Models/RolesDocument.cs @@ -2,11 +2,10 @@ using OrchardCore.Data.Documents; using OrchardCore.Security; -namespace OrchardCore.Roles.Models +namespace OrchardCore.Roles.Models; + +public class RolesDocument : Document { - public class RolesDocument : Document - { - public List Roles { get; set; } = []; - public Dictionary> MissingFeaturesByRole { get; set; } = []; - } + public List Roles { get; set; } = []; + public Dictionary> MissingFeaturesByRole { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Recipes/RolesStep.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Recipes/RolesStep.cs index 8488e4fc0d5..0f66ded8ba9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Recipes/RolesStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Recipes/RolesStep.cs @@ -8,76 +8,75 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.Roles.Recipes +namespace OrchardCore.Roles.Recipes; + +/// +/// This recipe step creates a set of roles. +/// +public sealed class RolesStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of roles. - /// - public sealed class RolesStep : IRecipeStepHandler + private readonly RoleManager _roleManager; + + public RolesStep(RoleManager roleManager) { - private readonly RoleManager _roleManager; + _roleManager = roleManager; + } - public RolesStep(RoleManager roleManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Roles", StringComparison.OrdinalIgnoreCase)) { - _roleManager = roleManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + var model = context.Step.ToObject(); + + foreach (var importedRole in model.Roles) { - if (!string.Equals(context.Name, "Roles", StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(importedRole.Name)) { - return; + continue; } - var model = context.Step.ToObject(); + var role = (Role)await _roleManager.FindByNameAsync(importedRole.Name); + var isNewRole = role == null; - foreach (var importedRole in model.Roles) + if (isNewRole) { - if (string.IsNullOrWhiteSpace(importedRole.Name)) - { - continue; - } - - var role = (Role)await _roleManager.FindByNameAsync(importedRole.Name); - var isNewRole = role == null; - - if (isNewRole) + role = new Role { - role = new Role - { - RoleName = importedRole.Name - }; - } + RoleName = importedRole.Name + }; + } - role.RoleDescription = importedRole.Description; - role.RoleClaims.RemoveAll(c => c.ClaimType == Permission.ClaimType); - role.RoleClaims.AddRange(importedRole.Permissions.Select(p => new RoleClaim - { - ClaimType = Permission.ClaimType, - ClaimValue = p, - })); + role.RoleDescription = importedRole.Description; + role.RoleClaims.RemoveAll(c => c.ClaimType == Permission.ClaimType); + role.RoleClaims.AddRange(importedRole.Permissions.Select(p => new RoleClaim + { + ClaimType = Permission.ClaimType, + ClaimValue = p, + })); - if (isNewRole) - { - await _roleManager.CreateAsync(role); - } - else - { - await _roleManager.UpdateAsync(role); - } + if (isNewRole) + { + await _roleManager.CreateAsync(role); + } + else + { + await _roleManager.UpdateAsync(role); } } } +} - public sealed class RolesStepModel - { - public RolesStepRoleModel[] Roles { get; set; } - } +public sealed class RolesStepModel +{ + public RolesStepRoleModel[] Roles { get; set; } +} - public sealed class RolesStepRoleModel - { - public string Name { get; set; } - public string Description { get; set; } - public string[] Permissions { get; set; } - } +public sealed class RolesStepRoleModel +{ + public string Name { get; set; } + public string Description { get; set; } + public string[] Permissions { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs index 8b8ac87027f..3f520e752e1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleStore.cs @@ -13,219 +13,218 @@ using OrchardCore.Roles.Models; using OrchardCore.Security; -namespace OrchardCore.Roles.Services +namespace OrchardCore.Roles.Services; + +public class RoleStore : IRoleClaimStore, IQueryableRoleStore { - public class RoleStore : IRoleClaimStore, IQueryableRoleStore + private readonly IServiceProvider _serviceProvider; + private readonly IDocumentManager _documentManager; + protected readonly IStringLocalizer S; + private readonly ILogger _logger; + + private bool _updating; + + public RoleStore( + IServiceProvider serviceProvider, + IDocumentManager documentManager, + IStringLocalizer stringLocalizer, + ILogger logger) { - private readonly IServiceProvider _serviceProvider; - private readonly IDocumentManager _documentManager; - protected readonly IStringLocalizer S; - private readonly ILogger _logger; + _serviceProvider = serviceProvider; + _documentManager = documentManager; + S = stringLocalizer; + _logger = logger; + } - private bool _updating; + public IQueryable Roles => GetRolesAsync().GetAwaiter().GetResult().Roles.AsQueryable(); - public RoleStore( - IServiceProvider serviceProvider, - IDocumentManager documentManager, - IStringLocalizer stringLocalizer, - ILogger logger) - { - _serviceProvider = serviceProvider; - _documentManager = documentManager; - S = stringLocalizer; - _logger = logger; - } + /// + /// Loads the roles document from the store for updating and that should not be cached. + /// + private Task LoadRolesAsync() => _documentManager.GetOrCreateMutableAsync(); - public IQueryable Roles => GetRolesAsync().GetAwaiter().GetResult().Roles.AsQueryable(); + /// + /// Gets the roles document from the cache for sharing and that should not be updated. + /// + private Task GetRolesAsync() => _documentManager.GetOrCreateImmutableAsync(); - /// - /// Loads the roles document from the store for updating and that should not be cached. - /// - private Task LoadRolesAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Updates the store with the provided roles document and then updates the cache. + /// + private Task UpdateRolesAsync(RolesDocument roles) + { + _updating = true; - /// - /// Gets the roles document from the cache for sharing and that should not be updated. - /// - private Task GetRolesAsync() => _documentManager.GetOrCreateImmutableAsync(); + return _documentManager.UpdateAsync(roles); + } - /// - /// Updates the store with the provided roles document and then updates the cache. - /// - private Task UpdateRolesAsync(RolesDocument roles) - { - _updating = true; + #region IRoleStore - return _documentManager.UpdateAsync(roles); - } + public async Task CreateAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - #region IRoleStore + var roleToCreate = (Role)role; - public async Task CreateAsync(IRole role, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + var roles = await LoadRolesAsync(); + roles.Roles.Add(roleToCreate); + await UpdateRolesAsync(roles); - var roleToCreate = (Role)role; + var roleCreatedEventHandlers = _serviceProvider.GetRequiredService>(); - var roles = await LoadRolesAsync(); - roles.Roles.Add(roleToCreate); - await UpdateRolesAsync(roles); + await roleCreatedEventHandlers.InvokeAsync((handler, roleToCreate) => + handler.RoleCreatedAsync(roleToCreate.RoleName), roleToCreate, _logger); - var roleCreatedEventHandlers = _serviceProvider.GetRequiredService>(); + return IdentityResult.Success; + } - await roleCreatedEventHandlers.InvokeAsync((handler, roleToCreate) => - handler.RoleCreatedAsync(roleToCreate.RoleName), roleToCreate, _logger); + public async Task DeleteAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - return IdentityResult.Success; - } + var roleToRemove = (Role)role; - public async Task DeleteAsync(IRole role, CancellationToken cancellationToken) + if (string.Equals(roleToRemove.NormalizedRoleName, "ANONYMOUS", StringComparison.Ordinal) || + string.Equals(roleToRemove.NormalizedRoleName, "AUTHENTICATED", StringComparison.Ordinal)) { - ArgumentNullException.ThrowIfNull(role); + return IdentityResult.Failed(new IdentityError { Description = S["Can't delete system roles."] }); + } - var roleToRemove = (Role)role; + var roleRemovedEventHandlers = _serviceProvider.GetRequiredService>(); - if (string.Equals(roleToRemove.NormalizedRoleName, "ANONYMOUS", StringComparison.Ordinal) || - string.Equals(roleToRemove.NormalizedRoleName, "AUTHENTICATED", StringComparison.Ordinal)) - { - return IdentityResult.Failed(new IdentityError { Description = S["Can't delete system roles."] }); - } + await roleRemovedEventHandlers.InvokeAsync((handler, roleToRemove) => + handler.RoleRemovedAsync(roleToRemove.RoleName), roleToRemove, _logger); - var roleRemovedEventHandlers = _serviceProvider.GetRequiredService>(); + var roles = await LoadRolesAsync(); + roleToRemove = roles.Roles.FirstOrDefault(r => string.Equals(r.RoleName, roleToRemove.RoleName, StringComparison.OrdinalIgnoreCase)); + roles.Roles.Remove(roleToRemove); - await roleRemovedEventHandlers.InvokeAsync((handler, roleToRemove) => - handler.RoleRemovedAsync(roleToRemove.RoleName), roleToRemove, _logger); + await UpdateRolesAsync(roles); - var roles = await LoadRolesAsync(); - roleToRemove = roles.Roles.FirstOrDefault(r => string.Equals(r.RoleName, roleToRemove.RoleName, StringComparison.OrdinalIgnoreCase)); - roles.Roles.Remove(roleToRemove); + return IdentityResult.Success; + } - await UpdateRolesAsync(roles); + public async Task FindByIdAsync(string roleId, CancellationToken cancellationToken) + { + // While updating find a role from the loaded document being mutated. + var roles = _updating ? await LoadRolesAsync() : await GetRolesAsync(); - return IdentityResult.Success; - } + var role = roles.Roles.FirstOrDefault(x => string.Equals(x.RoleName, roleId, StringComparison.OrdinalIgnoreCase)); - public async Task FindByIdAsync(string roleId, CancellationToken cancellationToken) + if (role == null) { - // While updating find a role from the loaded document being mutated. - var roles = _updating ? await LoadRolesAsync() : await GetRolesAsync(); - - var role = roles.Roles.FirstOrDefault(x => string.Equals(x.RoleName, roleId, StringComparison.OrdinalIgnoreCase)); - - if (role == null) - { - return null; - } - - return _updating ? role : role.Clone(); + return null; } - public async Task FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken) - { - // While updating find a role from the loaded document being mutated. - var roles = _updating ? await LoadRolesAsync() : await GetRolesAsync(); + return _updating ? role : role.Clone(); + } - var role = roles.Roles.FirstOrDefault(x => x.NormalizedRoleName == normalizedRoleName); + public async Task FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken) + { + // While updating find a role from the loaded document being mutated. + var roles = _updating ? await LoadRolesAsync() : await GetRolesAsync(); - if (role == null) - { - return null; - } + var role = roles.Roles.FirstOrDefault(x => x.NormalizedRoleName == normalizedRoleName); - return _updating ? role : role.Clone(); + if (role == null) + { + return null; } - public Task GetNormalizedRoleNameAsync(IRole role, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return _updating ? role : role.Clone(); + } - return Task.FromResult(((Role)role).NormalizedRoleName); - } + public Task GetNormalizedRoleNameAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - public Task GetRoleIdAsync(IRole role, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return Task.FromResult(((Role)role).NormalizedRoleName); + } - return Task.FromResult(role.RoleName.ToUpperInvariant()); - } + public Task GetRoleIdAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - public Task GetRoleNameAsync(IRole role, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return Task.FromResult(role.RoleName.ToUpperInvariant()); + } - return Task.FromResult(role.RoleName); - } + public Task GetRoleNameAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - public Task SetNormalizedRoleNameAsync(IRole role, string normalizedName, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return Task.FromResult(role.RoleName); + } - ((Role)role).NormalizedRoleName = normalizedName; + public Task SetNormalizedRoleNameAsync(IRole role, string normalizedName, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - return Task.CompletedTask; - } + ((Role)role).NormalizedRoleName = normalizedName; - public Task SetRoleNameAsync(IRole role, string roleName, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return Task.CompletedTask; + } - ((Role)role).RoleName = roleName; + public Task SetRoleNameAsync(IRole role, string roleName, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - return Task.CompletedTask; - } + ((Role)role).RoleName = roleName; - public async Task UpdateAsync(IRole role, CancellationToken cancellationToken) - { - ArgumentNullException.ThrowIfNull(role); + return Task.CompletedTask; + } - var roles = await LoadRolesAsync(); - var existingRole = roles.Roles.FirstOrDefault(x => string.Equals(x.RoleName, role.RoleName, StringComparison.OrdinalIgnoreCase)); - roles.Roles.Remove(existingRole); - roles.Roles.Add((Role)role); + public async Task UpdateAsync(IRole role, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(role); - await UpdateRolesAsync(roles); + var roles = await LoadRolesAsync(); + var existingRole = roles.Roles.FirstOrDefault(x => string.Equals(x.RoleName, role.RoleName, StringComparison.OrdinalIgnoreCase)); + roles.Roles.Remove(existingRole); + roles.Roles.Add((Role)role); - return IdentityResult.Success; - } + await UpdateRolesAsync(roles); - #endregion IRoleStore + return IdentityResult.Success; + } - #region IRoleClaimStore + #endregion IRoleStore - public Task AddClaimAsync(IRole role, Claim claim, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(role); + #region IRoleClaimStore - ArgumentNullException.ThrowIfNull(claim); + public Task AddClaimAsync(IRole role, Claim claim, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(role); - ((Role)role).RoleClaims.Add(new RoleClaim { ClaimType = claim.Type, ClaimValue = claim.Value }); + ArgumentNullException.ThrowIfNull(claim); - return Task.CompletedTask; - } + ((Role)role).RoleClaims.Add(new RoleClaim { ClaimType = claim.Type, ClaimValue = claim.Value }); - public Task> GetClaimsAsync(IRole role, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(role); + return Task.CompletedTask; + } - return Task.FromResult>(((Role)role).RoleClaims.Select(x => x.ToClaim()).ToList()); - } + public Task> GetClaimsAsync(IRole role, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(role); - public Task RemoveClaimAsync(IRole role, Claim claim, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(role); + return Task.FromResult>(((Role)role).RoleClaims.Select(x => x.ToClaim()).ToList()); + } + + public Task RemoveClaimAsync(IRole role, Claim claim, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(role); - ArgumentNullException.ThrowIfNull(claim); + ArgumentNullException.ThrowIfNull(claim); - ((Role)role).RoleClaims.RemoveAll(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value); + ((Role)role).RoleClaims.RemoveAll(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value); - return Task.CompletedTask; - } + return Task.CompletedTask; + } - #endregion IRoleClaimStore + #endregion IRoleClaimStore #pragma warning disable CA1816 - public void Dispose() - { - } -#pragma warning restore CA1816 + public void Dispose() + { } +#pragma warning restore CA1816 } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs index c4440bbfb87..65cdf3a92e4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RoleUpdater.cs @@ -12,220 +12,219 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.Roles.Services +namespace OrchardCore.Roles.Services; + +public class RoleUpdater : FeatureEventHandler, IRoleCreatedEventHandler, IRoleRemovedEventHandler { - public class RoleUpdater : FeatureEventHandler, IRoleCreatedEventHandler, IRoleRemovedEventHandler + private readonly ShellDescriptor _shellDescriptor; + private readonly IExtensionManager _extensionManager; + private readonly IDocumentManager _documentManager; + private readonly IEnumerable _permissionProviders; + private readonly ITypeFeatureProvider _typeFeatureProvider; + private readonly ILogger _logger; + + private readonly HashSet _installedFeatures = []; + + public RoleUpdater( + ShellDescriptor shellDescriptor, + IExtensionManager extensionManager, + IDocumentManager documentManager, + IEnumerable permissionProviders, + ITypeFeatureProvider typeFeatureProvider, + ILogger logger) { - private readonly ShellDescriptor _shellDescriptor; - private readonly IExtensionManager _extensionManager; - private readonly IDocumentManager _documentManager; - private readonly IEnumerable _permissionProviders; - private readonly ITypeFeatureProvider _typeFeatureProvider; - private readonly ILogger _logger; - - private readonly HashSet _installedFeatures = []; - - public RoleUpdater( - ShellDescriptor shellDescriptor, - IExtensionManager extensionManager, - IDocumentManager documentManager, - IEnumerable permissionProviders, - ITypeFeatureProvider typeFeatureProvider, - ILogger logger) - { - _shellDescriptor = shellDescriptor; - _extensionManager = extensionManager; - _documentManager = documentManager; - _permissionProviders = permissionProviders; - _typeFeatureProvider = typeFeatureProvider; - _logger = logger; - } + _shellDescriptor = shellDescriptor; + _extensionManager = extensionManager; + _documentManager = documentManager; + _permissionProviders = permissionProviders; + _typeFeatureProvider = typeFeatureProvider; + _logger = logger; + } - public override Task InstalledAsync(IFeatureInfo feature) => UpdateRolesForInstalledFeatureAsync(feature); + public override Task InstalledAsync(IFeatureInfo feature) => UpdateRolesForInstalledFeatureAsync(feature); - public override Task EnabledAsync(IFeatureInfo feature) => UpdateRolesForEnabledFeatureAsync(feature); + public override Task EnabledAsync(IFeatureInfo feature) => UpdateRolesForEnabledFeatureAsync(feature); - public Task RoleCreatedAsync(string roleName) => UpdateRoleForInstalledFeaturesAsync(roleName); + public Task RoleCreatedAsync(string roleName) => UpdateRoleForInstalledFeaturesAsync(roleName); - public Task RoleRemovedAsync(string roleName) => RemoveRoleForMissingFeaturesAsync(roleName); + public Task RoleRemovedAsync(string roleName) => RemoveRoleForMissingFeaturesAsync(roleName); - private async Task UpdateRolesForInstalledFeatureAsync(IFeatureInfo feature) - { - _installedFeatures.Add(feature.Id); + private async Task UpdateRolesForInstalledFeatureAsync(IFeatureInfo feature) + { + _installedFeatures.Add(feature.Id); - var providers = _permissionProviders - .Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id)); + var providers = _permissionProviders + .Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id)); - if (!providers.Any()) - { - return; - } + if (!providers.Any()) + { + return; + } - var updated = false; - var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); - foreach (var provider in providers) + var updated = false; + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + foreach (var provider in providers) + { + var stereotypes = provider.GetDefaultStereotypes(); + foreach (var stereotype in stereotypes) { - var stereotypes = provider.GetDefaultStereotypes(); - foreach (var stereotype in stereotypes) + var role = rolesDocument.Roles.FirstOrDefault(role => string.Equals(role.RoleName, stereotype.Name, StringComparison.OrdinalIgnoreCase)); + if (role == null) { - var role = rolesDocument.Roles.FirstOrDefault(role => string.Equals(role.RoleName, stereotype.Name, StringComparison.OrdinalIgnoreCase)); - if (role == null) - { - continue; - } - - var permissions = (stereotype.Permissions ?? []) - .Select(stereotype => stereotype.Name); - - if (UpdateRole(role, permissions, _logger)) - { - updated = true; - } + continue; } - } - if (updated) - { - await _documentManager.UpdateAsync(rolesDocument); + var permissions = (stereotype.Permissions ?? []) + .Select(stereotype => stereotype.Name); + + if (UpdateRole(role, permissions, _logger)) + { + updated = true; + } } } - private async Task UpdateRolesForEnabledFeatureAsync(IFeatureInfo feature) + if (updated) { - if (_installedFeatures.Contains(feature.Id)) - { - return; - } - - var providers = _permissionProviders - .Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id)); - - if (!providers.Any()) - { - return; - } - - var updated = false; - var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); - foreach (var role in rolesDocument.Roles) - { - if (!rolesDocument.MissingFeaturesByRole.TryGetValue(role.RoleName, out var missingFeatures) || - !missingFeatures.Contains(feature.Id)) - { - continue; - } + await _documentManager.UpdateAsync(rolesDocument); + } + } - updated = true; + private async Task UpdateRolesForEnabledFeatureAsync(IFeatureInfo feature) + { + if (_installedFeatures.Contains(feature.Id)) + { + return; + } - missingFeatures.Remove(feature.Id); - UpdateRolesForEnabledFeature(role, providers, _logger); - } + var providers = _permissionProviders + .Where(provider => _typeFeatureProvider.GetFeaturesForDependency(provider.GetType()).Any(p => p.Id == feature.Id)); - if (updated) - { - await _documentManager.UpdateAsync(rolesDocument); - } + if (!providers.Any()) + { + return; } - private async Task UpdateRoleForInstalledFeaturesAsync(string roleName) + var updated = false; + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + foreach (var role in rolesDocument.Roles) { - var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); - var role = rolesDocument.Roles.FirstOrDefault(role => string.Equals(role.RoleName, roleName, StringComparison.OrdinalIgnoreCase)); - if (role == null) + if (!rolesDocument.MissingFeaturesByRole.TryGetValue(role.RoleName, out var missingFeatures) || + !missingFeatures.Contains(feature.Id)) { - return; + continue; } - // Get installed features that are no more enabled. - var missingFeatures = _shellDescriptor.Installed - .Except(_shellDescriptor.Features) - .Select(feature => feature.Id) - .ToArray(); + updated = true; - // And defining at least one 'IPermissionProvider'. - rolesDocument.MissingFeaturesByRole[roleName] = (await _extensionManager.LoadFeaturesAsync(missingFeatures)) - .Where(entry => _typeFeatureProvider.GetTypesForFeature(entry).Any(type => type.IsAssignableTo(typeof(IPermissionProvider)))) - .Select(entry => entry.Id) - .ToList(); + missingFeatures.Remove(feature.Id); + UpdateRolesForEnabledFeature(role, providers, _logger); + } + if (updated) + { await _documentManager.UpdateAsync(rolesDocument); + } + } + + private async Task UpdateRoleForInstalledFeaturesAsync(string roleName) + { + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + var role = rolesDocument.Roles.FirstOrDefault(role => string.Equals(role.RoleName, roleName, StringComparison.OrdinalIgnoreCase)); + if (role == null) + { + return; + } - var stereotypes = _permissionProviders - .SelectMany(provider => provider.GetDefaultStereotypes()) - .Where(stereotype => string.Equals(stereotype.Name, roleName, StringComparison.OrdinalIgnoreCase)); + // Get installed features that are no more enabled. + var missingFeatures = _shellDescriptor.Installed + .Except(_shellDescriptor.Features) + .Select(feature => feature.Id) + .ToArray(); - if (!stereotypes.Any()) - { - return; - } + // And defining at least one 'IPermissionProvider'. + rolesDocument.MissingFeaturesByRole[roleName] = (await _extensionManager.LoadFeaturesAsync(missingFeatures)) + .Where(entry => _typeFeatureProvider.GetTypesForFeature(entry).Any(type => type.IsAssignableTo(typeof(IPermissionProvider)))) + .Select(entry => entry.Id) + .ToList(); - var permissions = stereotypes - .SelectMany(stereotype => stereotype.Permissions ?? []) - .Select(stereotype => stereotype.Name); + await _documentManager.UpdateAsync(rolesDocument); - UpdateRole(role, permissions, _logger); - } + var stereotypes = _permissionProviders + .SelectMany(provider => provider.GetDefaultStereotypes()) + .Where(stereotype => string.Equals(stereotype.Name, roleName, StringComparison.OrdinalIgnoreCase)); - private async Task RemoveRoleForMissingFeaturesAsync(string roleName) + if (!stereotypes.Any()) { - var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); - if (rolesDocument.MissingFeaturesByRole.TryGetValue(roleName, out _)) - { - rolesDocument.MissingFeaturesByRole.Remove(roleName); - await _documentManager.UpdateAsync(rolesDocument); - } + return; } - private static bool UpdateRolesForEnabledFeature(Role role, IEnumerable providers, ILogger logger) - { - var stereotypes = providers - .SelectMany(provider => provider.GetDefaultStereotypes()) - .Where(stereotype => string.Equals(stereotype.Name, role.RoleName, StringComparison.OrdinalIgnoreCase)); + var permissions = stereotypes + .SelectMany(stereotype => stereotype.Permissions ?? []) + .Select(stereotype => stereotype.Name); - if (!stereotypes.Any()) - { - return false; - } + UpdateRole(role, permissions, _logger); + } - var permissions = stereotypes - .SelectMany(stereotype => stereotype.Permissions ?? []) - .Select(stereotype => stereotype.Name); + private async Task RemoveRoleForMissingFeaturesAsync(string roleName) + { + var rolesDocument = await _documentManager.GetOrCreateMutableAsync(); + if (rolesDocument.MissingFeaturesByRole.TryGetValue(roleName, out _)) + { + rolesDocument.MissingFeaturesByRole.Remove(roleName); + await _documentManager.UpdateAsync(rolesDocument); + } + } - if (!permissions.Any()) - { - return false; - } + private static bool UpdateRolesForEnabledFeature(Role role, IEnumerable providers, ILogger logger) + { + var stereotypes = providers + .SelectMany(provider => provider.GetDefaultStereotypes()) + .Where(stereotype => string.Equals(stereotype.Name, role.RoleName, StringComparison.OrdinalIgnoreCase)); - return UpdateRole(role, permissions, logger); + if (!stereotypes.Any()) + { + return false; } - private static bool UpdateRole(Role role, IEnumerable permissions, ILogger logger) + var permissions = stereotypes + .SelectMany(stereotype => stereotype.Permissions ?? []) + .Select(stereotype => stereotype.Name); + + if (!permissions.Any()) { - var currentPermissions = role.RoleClaims - .Where(roleClaim => roleClaim.ClaimType == Permission.ClaimType) - .Select(roleClaim => roleClaim.ClaimValue); + return false; + } - var distinctPermissions = currentPermissions - .Union(permissions) - .Distinct(); + return UpdateRole(role, permissions, logger); + } - var additionalPermissions = distinctPermissions.Except(currentPermissions); - if (!additionalPermissions.Any()) - { - return false; - } + private static bool UpdateRole(Role role, IEnumerable permissions, ILogger logger) + { + var currentPermissions = role.RoleClaims + .Where(roleClaim => roleClaim.ClaimType == Permission.ClaimType) + .Select(roleClaim => roleClaim.ClaimValue); - foreach (var permission in additionalPermissions) - { - if (logger.IsEnabled(LogLevel.Debug)) - { - logger.LogDebug("Default role '{RoleName}' granted permission '{PermissionName}'.", role.RoleName, permission); - } + var distinctPermissions = currentPermissions + .Union(permissions) + .Distinct(); - role.RoleClaims.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permission }); + var additionalPermissions = distinctPermissions.Except(currentPermissions); + if (!additionalPermissions.Any()) + { + return false; + } + + foreach (var permission in additionalPermissions) + { + if (logger.IsEnabled(LogLevel.Debug)) + { + logger.LogDebug("Default role '{RoleName}' granted permission '{PermissionName}'.", role.RoleName, permission); } - return true; + role.RoleClaims.Add(new RoleClaim { ClaimType = Permission.ClaimType, ClaimValue = permission }); } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RolesPermissionsHandler.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RolesPermissionsHandler.cs index e3ee93a31d2..d328be3ff3e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Services/RolesPermissionsHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Services/RolesPermissionsHandler.cs @@ -5,67 +5,66 @@ using Microsoft.AspNetCore.Identity; using OrchardCore.Security; -namespace OrchardCore.Roles +namespace OrchardCore.Roles; + +/// +/// This authorization handler ensures that Anonymous and Authenticated permissions are checked. +/// +public class RolesPermissionsHandler : AuthorizationHandler { - /// - /// This authorization handler ensures that Anonymous and Authenticated permissions are checked. - /// - public class RolesPermissionsHandler : AuthorizationHandler - { - private readonly RoleManager _roleManager; - private readonly IPermissionGrantingService _permissionGrantingService; + private readonly RoleManager _roleManager; + private readonly IPermissionGrantingService _permissionGrantingService; + + private IEnumerable _anonymousClaims; + private IEnumerable _authenticatedClaims; - private IEnumerable _anonymousClaims; - private IEnumerable _authenticatedClaims; + public RolesPermissionsHandler( + RoleManager roleManager, + IPermissionGrantingService permissionGrantingService) + { + _roleManager = roleManager; + _permissionGrantingService = permissionGrantingService; + } - public RolesPermissionsHandler( - RoleManager roleManager, - IPermissionGrantingService permissionGrantingService) + protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + if (context.HasSucceeded) { - _roleManager = roleManager; - _permissionGrantingService = permissionGrantingService; + // This handler is not revoking any pre-existing grants. + return; } - protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + var claims = new HashSet(); + foreach (var claim in _anonymousClaims ??= await GetRoleClaimsAsync(OrchardCoreConstants.Roles.Anonymous)) { - if (context.HasSucceeded) - { - // This handler is not revoking any pre-existing grants. - return; - } + claims.Add(claim); + } - var claims = new HashSet(); - foreach (var claim in _anonymousClaims ??= await GetRoleClaimsAsync(OrchardCoreConstants.Roles.Anonymous)) + if (context.User.Identity.IsAuthenticated) + { + foreach (var claim in _authenticatedClaims ??= await GetRoleClaimsAsync(OrchardCoreConstants.Roles.Authenticated)) { claims.Add(claim); } - - if (context.User.Identity.IsAuthenticated) - { - foreach (var claim in _authenticatedClaims ??= await GetRoleClaimsAsync(OrchardCoreConstants.Roles.Authenticated)) - { - claims.Add(claim); - } - } - - if (_permissionGrantingService.IsGranted(requirement, claims)) - { - context.Succeed(requirement); - return; - } } - private async Task> GetRoleClaimsAsync(string roleName) + if (_permissionGrantingService.IsGranted(requirement, claims)) { - var role = await _roleManager.FindByNameAsync(roleName); + context.Succeed(requirement); + return; + } + } - if (role != null) - { - return ((Role)role).RoleClaims; - } + private async Task> GetRoleClaimsAsync(string roleName) + { + var role = await _roleManager.FindByNameAsync(roleName); - return []; + if (role != null) + { + return ((Role)role).RoleClaims; } + + return []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs index 7d15ae2fda8..38984dc3784 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/Startup.cs @@ -14,44 +14,43 @@ using OrchardCore.Security.Permissions; using OrchardCore.Security.Services; -namespace OrchardCore.Roles +namespace OrchardCore.Roles; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); - services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); + services.AddScoped(); + services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); + services.Replace(ServiceDescriptor.Scoped>(sp => sp.GetRequiredService())); - services.AddRecipeExecutionStep(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - } + services.AddRecipeExecutionStep(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } +} - [Feature("OrchardCore.Roles.Core")] - public sealed class RoleUpdaterStartup : StartupBase +[Feature("OrchardCore.Roles.Core")] +public sealed class RoleUpdaterStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped>(); - services.AddScoped(); + services.AddScoped>(); + services.AddScoped(); - services.AddScoped(); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); - services.AddScoped(sp => sp.GetRequiredService()); - } + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); + services.AddScoped(sp => sp.GetRequiredService()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/ViewComponents/SelectRolesViewComponent.cs b/src/OrchardCore.Modules/OrchardCore.Roles/ViewComponents/SelectRolesViewComponent.cs index 34dfddedfa3..18e1ca87685 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/ViewComponents/SelectRolesViewComponent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/ViewComponents/SelectRolesViewComponent.cs @@ -5,60 +5,59 @@ using Microsoft.AspNetCore.Mvc; using OrchardCore.Security.Services; -namespace OrchardCore.Roles.ViewComponents +namespace OrchardCore.Roles.ViewComponents; + +public class SelectRolesViewComponent : ViewComponent { - public class SelectRolesViewComponent : ViewComponent + private readonly IRoleService _roleService; + + public SelectRolesViewComponent(IRoleService roleService) { - private readonly IRoleService _roleService; + _roleService = roleService; + } - public SelectRolesViewComponent(IRoleService roleService) - { - _roleService = roleService; - } + public async Task InvokeAsync(IEnumerable selectedRoles, string htmlName, IEnumerable except = null) + { + selectedRoles ??= Array.Empty(); - public async Task InvokeAsync(IEnumerable selectedRoles, string htmlName, IEnumerable except = null) + var roleSelections = await BuildRoleSelectionsAsync(selectedRoles, except); + + var model = new SelectRolesViewModel { - selectedRoles ??= Array.Empty(); + HtmlName = htmlName, + RoleSelections = roleSelections + }; - var roleSelections = await BuildRoleSelectionsAsync(selectedRoles, except); + return View(model); + } - var model = new SelectRolesViewModel - { - HtmlName = htmlName, - RoleSelections = roleSelections - }; + private async Task>> BuildRoleSelectionsAsync(IEnumerable selectedRoles, IEnumerable except) + { + var roleNames = await _roleService.GetRoleNamesAsync(); - return View(model); + if (except != null) + { + roleNames = roleNames.Except(except, StringComparer.OrdinalIgnoreCase); } - private async Task>> BuildRoleSelectionsAsync(IEnumerable selectedRoles, IEnumerable except) + return roleNames.Select(x => new Selection { - var roleNames = await _roleService.GetRoleNamesAsync(); - - if (except != null) - { - roleNames = roleNames.Except(except, StringComparer.OrdinalIgnoreCase); - } - - return roleNames.Select(x => new Selection - { - IsSelected = selectedRoles.Contains(x), - Item = x - }) - .OrderBy(x => x.Item) - .ToList(); - } + IsSelected = selectedRoles.Contains(x), + Item = x + }) + .OrderBy(x => x.Item) + .ToList(); } +} - public class SelectRolesViewModel - { - public string HtmlName { get; set; } - public IList> RoleSelections { get; set; } - } +public class SelectRolesViewModel +{ + public string HtmlName { get; set; } + public IList> RoleSelections { get; set; } +} - public class Selection - { - public bool IsSelected { get; set; } - public T Item { get; set; } - } +public class Selection +{ + public bool IsSelected { get; set; } + public T Item { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/CreateRoleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/CreateRoleViewModel.cs index 9e0a534ded9..cad7c8724ec 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/CreateRoleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/CreateRoleViewModel.cs @@ -1,12 +1,11 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Roles.ViewModels +namespace OrchardCore.Roles.ViewModels; + +public class CreateRoleViewModel { - public class CreateRoleViewModel - { - [Required] - public string RoleName { get; set; } + [Required] + public string RoleName { get; set; } - public string RoleDescription { get; set; } - } + public string RoleDescription { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/EditRoleViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/EditRoleViewModel.cs index 279529f79fb..2e053b701fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/EditRoleViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/EditRoleViewModel.cs @@ -3,20 +3,19 @@ using OrchardCore.Security; using OrchardCore.Security.Permissions; -namespace OrchardCore.Roles.ViewModels +namespace OrchardCore.Roles.ViewModels; + +public class EditRoleViewModel { - public class EditRoleViewModel - { - public string Name { get; set; } - public string RoleDescription { get; set; } + public string Name { get; set; } + public string RoleDescription { get; set; } - [BindNever] - public IDictionary> RoleCategoryPermissions { get; set; } + [BindNever] + public IDictionary> RoleCategoryPermissions { get; set; } - [BindNever] - public IEnumerable EffectivePermissions { get; set; } + [BindNever] + public IEnumerable EffectivePermissions { get; set; } - [BindNever] - public Role Role { get; set; } - } + [BindNever] + public Role Role { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/RolesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/RolesViewModel.cs index 2bd26ef4d4f..d7f8595ce17 100644 --- a/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/RolesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Roles/ViewModels/RolesViewModel.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; -namespace OrchardCore.Roles.ViewModels +namespace OrchardCore.Roles.ViewModels; + +public class RolesViewModel { - public class RolesViewModel - { - public List RoleEntries { get; set; } = []; - } + public List RoleEntries { get; set; } = []; +} - public class RoleEntry - { - public string Name { get; set; } - public string Description { get; set; } - public bool Selected { get; set; } - } +public class RoleEntry +{ + public string Name { get; set; } + public string Description { get; set; } + public bool Selected { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AllConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AllConditionDisplayDriver.cs index 46622e2d554..459ca8a4a32 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AllConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AllConditionDisplayDriver.cs @@ -5,38 +5,37 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class AllConditionDisplayDriver : DisplayDriver { - public sealed class AllConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(AllConditionGroup condition, BuildDisplayContext context) { - public override Task DisplayAsync(AllConditionGroup condition, BuildDisplayContext context) - { - return - CombineAsync( - View("AllCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("AllCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content"), - Initialize("ConditionGroup_Fields_Summary", m => - { - m.Entries = condition.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); - m.Condition = condition; - }).Location("Summary", "Content") - ); - } + return + CombineAsync( + View("AllCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("AllCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content"), + Initialize("ConditionGroup_Fields_Summary", m => + { + m.Entries = condition.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); + m.Condition = condition; + }).Location("Summary", "Content") + ); + } - public override IDisplayResult Edit(AllConditionGroup condition, BuildEditorContext context) + public override IDisplayResult Edit(AllConditionGroup condition, BuildEditorContext context) + { + return Initialize("AllCondition_Fields_Edit", m => { - return Initialize("AllCondition_Fields_Edit", m => - { - m.DisplayText = condition.DisplayText; - m.Condition = condition; - }).Location("Content"); - } + m.DisplayText = condition.DisplayText; + m.Condition = condition; + }).Location("Content"); + } - public override async Task UpdateAsync(AllConditionGroup condition, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.DisplayText); + public override async Task UpdateAsync(AllConditionGroup condition, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.DisplayText); - return Edit(condition, context); - } + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AnyConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AnyConditionDisplayDriver.cs index de72483711e..30a6e30dae0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AnyConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/AnyConditionDisplayDriver.cs @@ -5,38 +5,37 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class AnyConditionDisplayDriver : DisplayDriver { - public sealed class AnyConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(AnyConditionGroup condition, BuildDisplayContext context) { - public override Task DisplayAsync(AnyConditionGroup condition, BuildDisplayContext context) - { - return - CombineAsync( - View("AnyCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("AnyCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content"), - Initialize("ConditionGroup_Fields_Summary", m => - { - m.Entries = condition.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); - m.Condition = condition; - }).Location("Summary", "Content") - ); - } + return + CombineAsync( + View("AnyCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("AnyCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content"), + Initialize("ConditionGroup_Fields_Summary", m => + { + m.Entries = condition.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); + m.Condition = condition; + }).Location("Summary", "Content") + ); + } - public override IDisplayResult Edit(AnyConditionGroup condition, BuildEditorContext context) + public override IDisplayResult Edit(AnyConditionGroup condition, BuildEditorContext context) + { + return Initialize("AnyCondition_Fields_Edit", m => { - return Initialize("AnyCondition_Fields_Edit", m => - { - m.DisplayText = condition.DisplayText; - m.Condition = condition; - }).Location("Content"); - } + m.DisplayText = condition.DisplayText; + m.Condition = condition; + }).Location("Content"); + } - public override async Task UpdateAsync(AnyConditionGroup condition, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.DisplayText); + public override async Task UpdateAsync(AnyConditionGroup condition, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.DisplayText); - return Edit(condition, context); - } + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/BooleanConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/BooleanConditionDisplayDriver.cs index b92af91edc0..2da93b08b58 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/BooleanConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/BooleanConditionDisplayDriver.cs @@ -4,33 +4,32 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class BooleanConditionDisplayDriver : DisplayDriver { - public sealed class BooleanConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(BooleanCondition condition, BuildDisplayContext context) { - public override Task DisplayAsync(BooleanCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("BooleanCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("BooleanCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("BooleanCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("BooleanCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(BooleanCondition condition, BuildEditorContext context) + public override IDisplayResult Edit(BooleanCondition condition, BuildEditorContext context) + { + return Initialize("BooleanCondition_Fields_Edit", m => { - return Initialize("BooleanCondition_Fields_Edit", m => - { - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } - public override async Task UpdateAsync(BooleanCondition condition, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.Value); + public override async Task UpdateAsync(BooleanCondition condition, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.Value); - return Edit(condition, context); - } + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionDisplayDriver.cs index 6814011b950..6144c36df8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionDisplayDriver.cs @@ -5,51 +5,50 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers -{ - public sealed class ContentTypeConditionDisplayDriver : DisplayDriver - { - private readonly ConditionOperatorOptions _options; +namespace OrchardCore.Rules.Drivers; - public ContentTypeConditionDisplayDriver(IOptions options) - { - _options = options.Value; - } +public sealed class ContentTypeConditionDisplayDriver : DisplayDriver +{ + private readonly ConditionOperatorOptions _options; - public override Task DisplayAsync(ContentTypeCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("ContentTypeCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("ContentTypeCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + public ContentTypeConditionDisplayDriver(IOptions options) + { + _options = options.Value; + } - public override IDisplayResult Edit(ContentTypeCondition condition, BuildEditorContext context) - { - return Initialize("ContentTypeCondition_Fields_Edit", m => - { - if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) - { - m.SelectedOperation = option.Factory.Name; - } - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } + public override Task DisplayAsync(ContentTypeCondition condition, BuildDisplayContext context) + { + return + CombineAsync( + View("ContentTypeCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("ContentTypeCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override async Task UpdateAsync(ContentTypeCondition condition, UpdateEditorContext context) + public override IDisplayResult Edit(ContentTypeCondition condition, BuildEditorContext context) + { + return Initialize("ContentTypeCondition_Fields_Edit", m => { - var model = new ContentTypeConditionViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - condition.Value = model.Value; - if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) { - condition.Operation = factory.Create(); + m.SelectedOperation = option.Factory.Name; } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } + + public override async Task UpdateAsync(ContentTypeCondition condition, UpdateEditorContext context) + { + var model = new ContentTypeConditionViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - return Edit(condition, context); + condition.Value = model.Value; + if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + { + condition.Operation = factory.Create(); } + + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionEvaluatorDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionEvaluatorDriver.cs index d881cb3902c..2240ae54512 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionEvaluatorDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/ContentTypeConditionEvaluatorDriver.cs @@ -9,57 +9,56 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.Services; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +/// +/// Saves references to content types which have been displayed during a request. +/// +public sealed class ContentTypeConditionEvaluatorDriver : ContentDisplayDriver, IConditionEvaluator { - /// - /// Saves references to content types which have been displayed during a request. - /// - public sealed class ContentTypeConditionEvaluatorDriver : ContentDisplayDriver, IConditionEvaluator - { - private readonly IConditionOperatorResolver _operatorResolver; + private readonly IConditionOperatorResolver _operatorResolver; + + // Hashset to prevent duplicate entries, but comparison is done by the comparers. + private readonly HashSet _contentTypes = []; - // Hashset to prevent duplicate entries, but comparison is done by the comparers. - private readonly HashSet _contentTypes = []; + public ContentTypeConditionEvaluatorDriver(IConditionOperatorResolver operatorResolver) + { + _operatorResolver = operatorResolver; + } - public ContentTypeConditionEvaluatorDriver(IConditionOperatorResolver operatorResolver) + public override Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) + { + // Do not include Widgets or any display type other than Detail. + if (context.DisplayType == "Detail" && (!context.Shape.TryGetProperty(nameof(ContentTypeSettings.Stereotype), out string stereotype) || stereotype != "Widget")) { - _operatorResolver = operatorResolver; + _contentTypes.Add(contentItem.ContentType); } - public override Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) - { - // Do not include Widgets or any display type other than Detail. - if (context.DisplayType == "Detail" && (!context.Shape.TryGetProperty(nameof(ContentTypeSettings.Stereotype), out string stereotype) || stereotype != "Widget")) - { - _contentTypes.Add(contentItem.ContentType); - } + return Task.FromResult(null); + } - return Task.FromResult(null); - } + public ValueTask EvaluateAsync(Condition condition) + => EvaluateAsync(condition as ContentTypeCondition); - public ValueTask EvaluateAsync(Condition condition) - => EvaluateAsync(condition as ContentTypeCondition); + private ValueTask EvaluateAsync(ContentTypeCondition condition) + { + var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); - private ValueTask EvaluateAsync(ContentTypeCondition condition) + // If no content types are considered, use the empty string as the value to compare against, + // since we still want comparisons such as "Does Not Equal", "Does Not Start With", etc. to evaluate to true in this case. + if (_contentTypes.Count == 0) { - var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); - - // If no content types are considered, use the empty string as the value to compare against, - // since we still want comparisons such as "Does Not Equal", "Does Not Start With", etc. to evaluate to true in this case. - if (_contentTypes.Count == 0) - { - return ValueTask.FromResult(operatorComparer.Compare(condition.Operation, string.Empty, condition.Value)); - } + return ValueTask.FromResult(operatorComparer.Compare(condition.Operation, string.Empty, condition.Value)); + } - foreach (var contentType in _contentTypes) + foreach (var contentType in _contentTypes) + { + if (operatorComparer.Compare(condition.Operation, contentType, condition.Value)) { - if (operatorComparer.Compare(condition.Operation, contentType, condition.Value)) - { - return ValueTask.FromResult(true); - } + return ValueTask.FromResult(true); } - - return ValueTask.FromResult(false); } + + return ValueTask.FromResult(false); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/CultureConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/CultureConditionDisplayDriver.cs index ef5a63dc019..c31bf1459a5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/CultureConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/CultureConditionDisplayDriver.cs @@ -5,51 +5,50 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class CultureConditionDisplayDriver : DisplayDriver { - public sealed class CultureConditionDisplayDriver : DisplayDriver - { - private readonly ConditionOperatorOptions _options; + private readonly ConditionOperatorOptions _options; - public CultureConditionDisplayDriver(IOptions options) - { - _options = options.Value; - } + public CultureConditionDisplayDriver(IOptions options) + { + _options = options.Value; + } - public override Task DisplayAsync(CultureCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("CultureCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("CultureCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + public override Task DisplayAsync(CultureCondition condition, BuildDisplayContext context) + { + return + CombineAsync( + View("CultureCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("CultureCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(CultureCondition condition, BuildEditorContext context) - { - return Initialize("CultureCondition_Fields_Edit", m => + public override IDisplayResult Edit(CultureCondition condition, BuildEditorContext context) + { + return Initialize("CultureCondition_Fields_Edit", m => + { + if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) { - if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) - { - m.SelectedOperation = option.Factory.Name; - } - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } - - public override async Task UpdateAsync(CultureCondition condition, UpdateEditorContext context) - { - var model = new CultureConditionViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + m.SelectedOperation = option.Factory.Name; + } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } - condition.Value = model.Value; - if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) - { - condition.Operation = factory.Create(); - } + public override async Task UpdateAsync(CultureCondition condition, UpdateEditorContext context) + { + var model = new CultureConditionViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - return Edit(condition, context); + condition.Value = model.Value; + if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + { + condition.Operation = factory.Create(); } + + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/HomepageConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/HomepageConditionDisplayDriver.cs index acc9d2eb6dd..356b1870ad1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/HomepageConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/HomepageConditionDisplayDriver.cs @@ -4,33 +4,32 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class HomepageConditionDisplayDriver : DisplayDriver { - public sealed class HomepageConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(HomepageCondition condition, BuildDisplayContext context) { - public override Task DisplayAsync(HomepageCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("HomepageCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("HomepageCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("HomepageCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("HomepageCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(HomepageCondition condition, BuildEditorContext context) + public override IDisplayResult Edit(HomepageCondition condition, BuildEditorContext context) + { + return Initialize("HomepageCondition_Fields_Edit", m => { - return Initialize("HomepageCondition_Fields_Edit", m => - { - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } - public override async Task UpdateAsync(HomepageCondition condition, UpdateEditorContext context) - { - await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.Value); + public override async Task UpdateAsync(HomepageCondition condition, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(condition, Prefix, x => x.Value); - return Edit(condition, context); - } + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAnonymousConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAnonymousConditionDisplayDriver.cs index 929866cef0d..c06313ef58b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAnonymousConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAnonymousConditionDisplayDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class IsAnonymousConditionDisplayDriver : DisplayDriver { - public sealed class IsAnonymousConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(IsAnonymousCondition condition, BuildDisplayContext context) { - public override Task DisplayAsync(IsAnonymousCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("IsAnonymousCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("IsAnonymousCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("IsAnonymousCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("IsAnonymousCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(IsAnonymousCondition condition, BuildEditorContext context) - { - return View("IsAnonymousCondition_Fields_Edit", condition).Location("Content"); - } + public override IDisplayResult Edit(IsAnonymousCondition condition, BuildEditorContext context) + { + return View("IsAnonymousCondition_Fields_Edit", condition).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAuthenticatedConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAuthenticatedConditionDisplayDriver.cs index 7866871f909..555fe982317 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAuthenticatedConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/IsAuthenticatedConditionDisplayDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class IsAuthenticatedConditionDisplayDriver : DisplayDriver { - public sealed class IsAuthenticatedConditionDisplayDriver : DisplayDriver + public override Task DisplayAsync(IsAuthenticatedCondition condition, BuildDisplayContext context) { - public override Task DisplayAsync(IsAuthenticatedCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("IsAuthenticatedCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("IsAuthenticatedCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("IsAuthenticatedCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("IsAuthenticatedCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(IsAuthenticatedCondition condition, BuildEditorContext context) - { - return View("IsAuthenticatedCondition_Fields_Edit", condition).Location("Content"); - } + public override IDisplayResult Edit(IsAuthenticatedCondition condition, BuildEditorContext context) + { + return View("IsAuthenticatedCondition_Fields_Edit", condition).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs index 8075fccb3b8..e0e6176256e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs @@ -12,88 +12,87 @@ using OrchardCore.Rules.Services; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class JavascriptConditionDisplayDriver : DisplayDriver { - public sealed class JavascriptConditionDisplayDriver : DisplayDriver + private readonly INotifier _notifier; + private readonly JavascriptConditionEvaluator _evaluator; + + internal readonly IHtmlLocalizer H; + internal readonly IStringLocalizer S; + + public JavascriptConditionDisplayDriver( + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + JavascriptConditionEvaluator evaluator, + INotifier notifier) { - private readonly INotifier _notifier; - private readonly JavascriptConditionEvaluator _evaluator; + H = htmlLocalizer; + S = stringLocalizer; + _evaluator = evaluator; + _notifier = notifier; + } - internal readonly IHtmlLocalizer H; - internal readonly IStringLocalizer S; + public override Task DisplayAsync(JavascriptCondition condition, BuildDisplayContext context) + { + return + CombineAsync( + View("JavascriptCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("JavascriptCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public JavascriptConditionDisplayDriver( - IHtmlLocalizer htmlLocalizer, - IStringLocalizer stringLocalizer, - JavascriptConditionEvaluator evaluator, - INotifier notifier) + public override IDisplayResult Edit(JavascriptCondition condition, BuildEditorContext context) + { + return Initialize("JavascriptCondition_Fields_Edit", m => { - H = htmlLocalizer; - S = stringLocalizer; - _evaluator = evaluator; - _notifier = notifier; - } + m.Script = condition.Script; + m.Condition = condition; + }).Location("Content"); + } + + public override async Task UpdateAsync(JavascriptCondition condition, UpdateEditorContext context) + { + var model = new JavascriptConditionViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - public override Task DisplayAsync(JavascriptCondition condition, BuildDisplayContext context) + // CodeMirror hides the textarea which displays the error when updater.ModelState.AddModelError() is used, + // that's why a notifier is used to show validation errors. + if (string.IsNullOrWhiteSpace(model.Script)) { - return - CombineAsync( - View("JavascriptCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("JavascriptCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["Please provide a script."]); + await _notifier.ErrorAsync(H["Please provide a script."]); + + return Edit(condition, context); } - public override IDisplayResult Edit(JavascriptCondition condition, BuildEditorContext context) + try { - return Initialize("JavascriptCondition_Fields_Edit", m => + _ = await _evaluator.EvaluateAsync(new() { - m.Script = condition.Script; - m.Condition = condition; - }).Location("Content"); + ConditionId = condition.ConditionId, + Name = condition.Name, + Script = model.Script + }); + condition.Script = model.Script; } - - public override async Task UpdateAsync(JavascriptCondition condition, UpdateEditorContext context) + catch (ParseErrorException ex) // Invalid syntax { - var model = new JavascriptConditionViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - - // CodeMirror hides the textarea which displays the error when updater.ModelState.AddModelError() is used, - // that's why a notifier is used to show validation errors. - if (string.IsNullOrWhiteSpace(model.Script)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["Please provide a script."]); - await _notifier.ErrorAsync(H["Please provide a script."]); - - return Edit(condition, context); - } - - try - { - _ = await _evaluator.EvaluateAsync(new() - { - ConditionId = condition.ConditionId, - Name = condition.Name, - Script = model.Script - }); - condition.Script = model.Script; - } - catch (ParseErrorException ex) // Invalid syntax - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script couldn't be parsed. Details: {0}", ex.Message]); - await _notifier.ErrorAsync(H["The script couldn't be parsed. Details: {0}", ex.Message]); - } - catch (JavaScriptException ex) // Evaluation threw an Error - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); - await _notifier.ErrorAsync(H["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); - } - catch (Exception ex) when (ex is InvalidCastException or FormatException) // Evaluation completes successfully, but the result cannot be converted to Boolean - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script evaluation failed. Details: {0}", ex.Message]); - await _notifier.ErrorAsync(H["The script evaluation failed. Details: {0}", ex.Message]); - } - - return Edit(condition, context); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script couldn't be parsed. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["The script couldn't be parsed. Details: {0}", ex.Message]); + } + catch (JavaScriptException ex) // Evaluation threw an Error + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); } + catch (Exception ex) when (ex is InvalidCastException or FormatException) // Evaluation completes successfully, but the result cannot be converted to Boolean + { + context.Updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script evaluation failed. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["The script evaluation failed. Details: {0}", ex.Message]); + } + + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RoleConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RoleConditionDisplayDriver.cs index 03801203b3f..1cd49c42bfa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RoleConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RoleConditionDisplayDriver.cs @@ -5,51 +5,50 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class RoleConditionDisplayDriver : DisplayDriver { - public sealed class RoleConditionDisplayDriver : DisplayDriver - { - private readonly ConditionOperatorOptions _options; + private readonly ConditionOperatorOptions _options; - public RoleConditionDisplayDriver(IOptions options) - { - _options = options.Value; - } + public RoleConditionDisplayDriver(IOptions options) + { + _options = options.Value; + } - public override Task DisplayAsync(RoleCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("RoleCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("RoleCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + public override Task DisplayAsync(RoleCondition condition, BuildDisplayContext context) + { + return + CombineAsync( + View("RoleCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("RoleCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(RoleCondition condition, BuildEditorContext context) - { - return Initialize("RoleCondition_Fields_Edit", m => + public override IDisplayResult Edit(RoleCondition condition, BuildEditorContext context) + { + return Initialize("RoleCondition_Fields_Edit", m => + { + if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) { - if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) - { - m.SelectedOperation = option.Factory.Name; - } - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } - - public override async Task UpdateAsync(RoleCondition condition, UpdateEditorContext context) - { - var model = new RoleConditionViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - condition.Value = model.Value; + m.SelectedOperation = option.Factory.Name; + } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } - if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) - { - condition.Operation = factory.Create(); - } + public override async Task UpdateAsync(RoleCondition condition, UpdateEditorContext context) + { + var model = new RoleConditionViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + condition.Value = model.Value; - return Edit(condition, context); + if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + { + condition.Operation = factory.Create(); } + + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RuleDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RuleDisplayDriver.cs index d393a4c9e53..f71b9ee93aa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RuleDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/RuleDisplayDriver.cs @@ -4,20 +4,19 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers +namespace OrchardCore.Rules.Drivers; + +public sealed class RuleDisplayDriver : DisplayDriver { - public sealed class RuleDisplayDriver : DisplayDriver + public override Task DisplayAsync(Rule rule, BuildDisplayContext context) { - public override Task DisplayAsync(Rule rule, BuildDisplayContext context) - { - return CombineAsync( - View("Rule_Fields_Summary", rule).Location("Summary", "Content"), - Initialize("ConditionGroup_Fields_Summary", m => - { - m.Entries = rule.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); - m.Condition = rule; - }).Location("Summary", "Content") - ); - } + return CombineAsync( + View("Rule_Fields_Summary", rule).Location("Summary", "Content"), + Initialize("ConditionGroup_Fields_Summary", m => + { + m.Entries = rule.Conditions.Select(x => new ConditionEntry { Condition = x }).ToArray(); + m.Condition = rule; + }).Location("Summary", "Content") + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/UrlConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/UrlConditionDisplayDriver.cs index 5f74ac21bf1..8576b2485a4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/UrlConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/UrlConditionDisplayDriver.cs @@ -5,51 +5,50 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.Drivers -{ - public sealed class UrlConditionDisplayDriver : DisplayDriver - { - private readonly ConditionOperatorOptions _options; +namespace OrchardCore.Rules.Drivers; - public UrlConditionDisplayDriver(IOptions options) - { - _options = options.Value; - } +public sealed class UrlConditionDisplayDriver : DisplayDriver +{ + private readonly ConditionOperatorOptions _options; - public override Task DisplayAsync(UrlCondition condition, BuildDisplayContext context) - { - return - CombineAsync( - View("UrlCondition_Fields_Summary", condition).Location("Summary", "Content"), - View("UrlCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") - ); - } + public UrlConditionDisplayDriver(IOptions options) + { + _options = options.Value; + } - public override IDisplayResult Edit(UrlCondition condition, BuildEditorContext context) - { - return Initialize("UrlCondition_Fields_Edit", m => - { - if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) - { - m.SelectedOperation = option.Factory.Name; - } - m.Value = condition.Value; - m.Condition = condition; - }).Location("Content"); - } + public override Task DisplayAsync(UrlCondition condition, BuildDisplayContext context) + { + return + CombineAsync( + View("UrlCondition_Fields_Summary", condition).Location("Summary", "Content"), + View("UrlCondition_Fields_Thumbnail", condition).Location("Thumbnail", "Content") + ); + } - public override async Task UpdateAsync(UrlCondition condition, UpdateEditorContext context) + public override IDisplayResult Edit(UrlCondition condition, BuildEditorContext context) + { + return Initialize("UrlCondition_Fields_Edit", m => { - var model = new UrlConditionViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); - condition.Value = model.Value; - - if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + if (condition.Operation != null && _options.ConditionOperatorOptionByType.TryGetValue(condition.Operation.GetType(), out var option)) { - condition.Operation = factory.Create(); + m.SelectedOperation = option.Factory.Name; } + m.Value = condition.Value; + m.Condition = condition; + }).Location("Content"); + } + + public override async Task UpdateAsync(UrlCondition condition, UpdateEditorContext context) + { + var model = new UrlConditionViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); + condition.Value = model.Value; - return Edit(condition, context); + if (!string.IsNullOrEmpty(model.SelectedOperation) && _options.Factories.TryGetValue(model.SelectedOperation, out var factory)) + { + condition.Operation = factory.Create(); } + + return Edit(condition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/AllConditionGroup.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/AllConditionGroup.cs index be35b1dc547..99dec096329 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/AllConditionGroup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/AllConditionGroup.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class AllConditionGroup : DisplayTextConditionGroup { - public class AllConditionGroup : DisplayTextConditionGroup - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/AnyConditionGroup.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/AnyConditionGroup.cs index 5b7d887f052..ad1a1c6459c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/AnyConditionGroup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/AnyConditionGroup.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class AnyConditionGroup : DisplayTextConditionGroup { - public class AnyConditionGroup : DisplayTextConditionGroup - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/BooleanCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/BooleanCondition.cs index 54608d773eb..5a8cc106aee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/BooleanCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/BooleanCondition.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class BooleanCondition : Condition { - public class BooleanCondition : Condition - { - public bool Value { get; set; } = true; - } + public bool Value { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/ContentTypeCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/ContentTypeCondition.cs index c6bc4801579..084752e9cb5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/ContentTypeCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/ContentTypeCondition.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class ContentTypeCondition : Condition { - public class ContentTypeCondition : Condition - { - public string Value { get; set; } - public ConditionOperator Operation { get; set; } - } + public string Value { get; set; } + public ConditionOperator Operation { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/CultureCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/CultureCondition.cs index 36c0978a957..fa0deb1ea5d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/CultureCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/CultureCondition.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class CultureCondition : Condition { - public class CultureCondition : Condition - { - public string Value { get; set; } - public ConditionOperator Operation { get; set; } - } + public string Value { get; set; } + public ConditionOperator Operation { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/HomepageCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/HomepageCondition.cs index b1ac78fc441..d7729d26c5b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/HomepageCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/HomepageCondition.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class HomepageCondition : Condition { - public class HomepageCondition : Condition - { - public bool Value { get; set; } = true; - } + public bool Value { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAnonymousCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAnonymousCondition.cs index 6da53467093..d07585e26b4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAnonymousCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAnonymousCondition.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class IsAnonymousCondition : Condition { - public class IsAnonymousCondition : Condition - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAuthenticatedCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAuthenticatedCondition.cs index e597654cb10..e5b69f8c153 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAuthenticatedCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/IsAuthenticatedCondition.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class IsAuthenticatedCondition : Condition { - public class IsAuthenticatedCondition : Condition - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/JavascriptCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/JavascriptCondition.cs index e15ad5c8f4f..2d884c30b71 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/JavascriptCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/JavascriptCondition.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class JavascriptCondition : Condition { - public class JavascriptCondition : Condition - { - public string Script { get; set; } - } + public string Script { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/RoleCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/RoleCondition.cs index 27b9ab182db..2787e2d8e2d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/RoleCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/RoleCondition.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class RoleCondition : Condition { - public class RoleCondition : Condition - { - public string Value { get; set; } - public ConditionOperator Operation { get; set; } - } + public string Value { get; set; } + public ConditionOperator Operation { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/StringOperators.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/StringOperators.cs index 38231a650c2..6f56ce28090 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/StringOperators.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/StringOperators.cs @@ -1,39 +1,38 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public abstract class StringOperator : ConditionOperator { - public abstract class StringOperator : ConditionOperator - { - public bool CaseSensitive { get; set; } - } + public bool CaseSensitive { get; set; } +} - public class StringEqualsOperator : StringOperator - { - } +public class StringEqualsOperator : StringOperator +{ +} - public class StringNotEqualsOperator : StringOperator, INegateOperator - { - } +public class StringNotEqualsOperator : StringOperator, INegateOperator +{ +} - public class StringStartsWithOperator : StringOperator - { - } +public class StringStartsWithOperator : StringOperator +{ +} - public class StringNotStartsWithOperator : StringOperator, INegateOperator - { - } +public class StringNotStartsWithOperator : StringOperator, INegateOperator +{ +} - public class StringEndsWithOperator : StringOperator - { - } +public class StringEndsWithOperator : StringOperator +{ +} - public class StringNotEndsWithOperator : StringOperator, INegateOperator - { - } +public class StringNotEndsWithOperator : StringOperator, INegateOperator +{ +} - public class StringContainsOperator : StringOperator - { - } +public class StringContainsOperator : StringOperator +{ +} - public class StringNotContainsOperator : StringOperator, INegateOperator - { - } +public class StringNotContainsOperator : StringOperator, INegateOperator +{ } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Models/UrlCondition.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Models/UrlCondition.cs index a2850c186f2..eae336ba264 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Models/UrlCondition.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Models/UrlCondition.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Rules.Models +namespace OrchardCore.Rules.Models; + +public class UrlCondition : Condition { - public class UrlCondition : Condition - { - public string Value { get; set; } - public ConditionOperator Operation { get; set; } - } + public string Value { get; set; } + public ConditionOperator Operation { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/AllConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/AllConditionEvaluator.cs index 9a6ab5e334d..5dc4e8a9b73 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/AllConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/AllConditionEvaluator.cs @@ -1,35 +1,34 @@ using System.Threading.Tasks; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class AllConditionEvaluator : ConditionEvaluator { - public class AllConditionEvaluator : ConditionEvaluator - { - private readonly IConditionResolver _conditionResolver; + private readonly IConditionResolver _conditionResolver; - public AllConditionEvaluator(IConditionResolver conditionResolver) - { - _conditionResolver = conditionResolver; - } + public AllConditionEvaluator(IConditionResolver conditionResolver) + { + _conditionResolver = conditionResolver; + } - public async override ValueTask EvaluateAsync(AllConditionGroup condition) + public async override ValueTask EvaluateAsync(AllConditionGroup condition) + { + foreach (var childCondition in condition.Conditions) { - foreach (var childCondition in condition.Conditions) + var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); + if (evaluator is null || !await evaluator.EvaluateAsync(childCondition)) { - var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); - if (evaluator is null || !await evaluator.EvaluateAsync(childCondition)) - { - return false; - } - } - - if (condition.Conditions.Count > 0) - { - return true; + return false; } + } - // This rule requires all conditions to be evaluated as true. - return false; + if (condition.Conditions.Count > 0) + { + return true; } + + // This rule requires all conditions to be evaluated as true. + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/AnyConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/AnyConditionEvaluator.cs index 2208270a29e..fc3593125d7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/AnyConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/AnyConditionEvaluator.cs @@ -1,29 +1,28 @@ using System.Threading.Tasks; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class AnyConditionEvaluator : ConditionEvaluator { - public class AnyConditionEvaluator : ConditionEvaluator - { - private readonly IConditionResolver _conditionResolver; + private readonly IConditionResolver _conditionResolver; - public AnyConditionEvaluator(IConditionResolver conditionResolver) - { - _conditionResolver = conditionResolver; - } + public AnyConditionEvaluator(IConditionResolver conditionResolver) + { + _conditionResolver = conditionResolver; + } - public async override ValueTask EvaluateAsync(AnyConditionGroup condition) + public async override ValueTask EvaluateAsync(AnyConditionGroup condition) + { + foreach (var childCondition in condition.Conditions) { - foreach (var childCondition in condition.Conditions) + var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); + if (evaluator is null || await evaluator.EvaluateAsync(childCondition)) { - var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); - if (evaluator is null || await evaluator.EvaluateAsync(childCondition)) - { - return true; - } + return true; } - - return false; } + + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/BooleanConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/BooleanConditionEvaluator.cs index 6070a993a78..eabfc31a6ca 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/BooleanConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/BooleanConditionEvaluator.cs @@ -1,11 +1,10 @@ using System.Threading.Tasks; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class BooleanConditionEvaluator : ConditionEvaluator { - public class BooleanConditionEvaluator : ConditionEvaluator - { - public override ValueTask EvaluateAsync(BooleanCondition condition) - => condition.Value ? True : False; - } + public override ValueTask EvaluateAsync(BooleanCondition condition) + => condition.Value ? True : False; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionIdGenerator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionIdGenerator.cs index 84e61bf09db..ae2262e3c4e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionIdGenerator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionIdGenerator.cs @@ -1,19 +1,18 @@ using OrchardCore.Entities; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class ConditionIdGenerator : IConditionIdGenerator { - public class ConditionIdGenerator : IConditionIdGenerator - { - private readonly IIdGenerator _idGenerator; + private readonly IIdGenerator _idGenerator; - public ConditionIdGenerator(IIdGenerator idGenerator) - { - _idGenerator = idGenerator; - } + public ConditionIdGenerator(IIdGenerator idGenerator) + { + _idGenerator = idGenerator; + } - public void GenerateUniqueId(Condition condition) - { - condition.ConditionId = _idGenerator.GenerateUniqueId(); - } + public void GenerateUniqueId(Condition condition) + { + condition.ConditionId = _idGenerator.GenerateUniqueId(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorConfigureOptions.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorConfigureOptions.cs index 6d73135e950..914bdc4c980 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorConfigureOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorConfigureOptions.cs @@ -1,63 +1,62 @@ using Microsoft.Extensions.Options; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public sealed class ConditionOperatorConfigureOptions : IConfigureOptions { - public sealed class ConditionOperatorConfigureOptions : IConfigureOptions + public void Configure(ConditionOperatorOptions options) { - public void Configure(ConditionOperatorOptions options) - { - options.Operators.AddRange( - [ - new ConditionOperatorOption( - (S) => S["Equals"], - new StringEqualsOperatorComparer(), - typeof(StringEqualsOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Does not equal"], - new StringNotEqualsOperatorComparer(), - typeof(StringNotEqualsOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Starts with"], - new StringStartsWithOperatorComparer(), - typeof(StringStartsWithOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Does not start with"], - new StringNotStartsWithOperatorComparer(), - typeof(StringNotStartsWithOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Ends with"], - new StringEndsWithOperatorComparer(), - typeof(StringEndsWithOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Does not end with"], - new StringNotEndsWithOperatorComparer(), - typeof(StringNotEndsWithOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Contains"], - new StringContainsOperatorComparer(), - typeof(StringContainsOperator), - new ConditionOperatorFactory() - ), - new ConditionOperatorOption( - (S) => S["Does not contain"], - new StringNotContainsOperatorComparer(), - typeof(StringNotContainsOperator), - new ConditionOperatorFactory() - ) - ]); - } + options.Operators.AddRange( + [ + new ConditionOperatorOption( + (S) => S["Equals"], + new StringEqualsOperatorComparer(), + typeof(StringEqualsOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Does not equal"], + new StringNotEqualsOperatorComparer(), + typeof(StringNotEqualsOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Starts with"], + new StringStartsWithOperatorComparer(), + typeof(StringStartsWithOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Does not start with"], + new StringNotStartsWithOperatorComparer(), + typeof(StringNotStartsWithOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Ends with"], + new StringEndsWithOperatorComparer(), + typeof(StringEndsWithOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Does not end with"], + new StringNotEndsWithOperatorComparer(), + typeof(StringNotEndsWithOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Contains"], + new StringContainsOperatorComparer(), + typeof(StringContainsOperator), + new ConditionOperatorFactory() + ), + new ConditionOperatorOption( + (S) => S["Does not contain"], + new StringNotContainsOperatorComparer(), + typeof(StringNotContainsOperator), + new ConditionOperatorFactory() + ) + ]); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorResolver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorResolver.cs index f5c977190d2..0ef482f6c24 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorResolver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionOperatorResolver.cs @@ -1,25 +1,24 @@ using System; using Microsoft.Extensions.Options; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class ConditionOperatorResolver : IConditionOperatorResolver { - public class ConditionOperatorResolver : IConditionOperatorResolver + private readonly ConditionOperatorOptions _options; + + public ConditionOperatorResolver(IOptions options) { - private readonly ConditionOperatorOptions _options; + _options = options.Value; + } - public ConditionOperatorResolver(IOptions options) + public IOperatorComparer GetOperatorComparer(ConditionOperator conditionOperator) + { + if (_options.ConditionOperatorOptionByType.TryGetValue(conditionOperator.GetType(), out var option)) { - _options = options.Value; + return option.Comparer; } - public IOperatorComparer GetOperatorComparer(ConditionOperator conditionOperator) - { - if (_options.ConditionOperatorOptionByType.TryGetValue(conditionOperator.GetType(), out var option)) - { - return option.Comparer; - } - - throw new InvalidOperationException($"Operator comparer for '{conditionOperator.GetType().Name}; not registered"); - } + throw new InvalidOperationException($"Operator comparer for '{conditionOperator.GetType().Name}; not registered"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionResolver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionResolver.cs index 83968e9254d..25f6e0946da 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionResolver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/ConditionResolver.cs @@ -2,29 +2,28 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class ConditionResolver : IConditionResolver { - public class ConditionResolver : IConditionResolver + private readonly IServiceProvider _serviceProvider; + private readonly ConditionOptions _options; + + public ConditionResolver(IServiceProvider serviceProvider, IOptions options) { - private readonly IServiceProvider _serviceProvider; - private readonly ConditionOptions _options; + _serviceProvider = serviceProvider; + _options = options.Value; + } - public ConditionResolver(IServiceProvider serviceProvider, IOptions options) + public IConditionEvaluator GetConditionEvaluator(Condition condition) + { + if (_options.Evaluators.TryGetValue(condition.GetType(), out var conditionEvaluatorType)) { - _serviceProvider = serviceProvider; - _options = options.Value; + return _serviceProvider.GetRequiredService(conditionEvaluatorType) as IConditionEvaluator; } - public IConditionEvaluator GetConditionEvaluator(Condition condition) - { - if (_options.Evaluators.TryGetValue(condition.GetType(), out var conditionEvaluatorType)) - { - return _serviceProvider.GetRequiredService(conditionEvaluatorType) as IConditionEvaluator; - } - - // throw new InvalidOperationException($"Condition evaluator for '{condition.GetType().Name}; not registered"); + // throw new InvalidOperationException($"Condition evaluator for '{condition.GetType().Name}; not registered"); - return null; - } + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/CultureConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/CultureConditionEvaluator.cs index 374ad81a726..30e9d7ebac9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/CultureConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/CultureConditionEvaluator.cs @@ -2,27 +2,26 @@ using System.Threading.Tasks; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class CultureConditionEvaluator : ConditionEvaluator { - public class CultureConditionEvaluator : ConditionEvaluator - { - private readonly IConditionOperatorResolver _operatorResolver; + private readonly IConditionOperatorResolver _operatorResolver; - public CultureConditionEvaluator(IConditionOperatorResolver operatorResolver) - { - _operatorResolver = operatorResolver; - } + public CultureConditionEvaluator(IConditionOperatorResolver operatorResolver) + { + _operatorResolver = operatorResolver; + } - public override ValueTask EvaluateAsync(CultureCondition condition) - { - var currentCulture = CultureInfo.CurrentCulture; + public override ValueTask EvaluateAsync(CultureCondition condition) + { + var currentCulture = CultureInfo.CurrentCulture; - var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); + var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); - var result = operatorComparer.Compare(condition.Operation, currentCulture.Name, condition.Value) || - operatorComparer.Compare(condition.Operation, currentCulture.Parent.Name, condition.Value); + var result = operatorComparer.Compare(condition.Operation, currentCulture.Name, condition.Value) || + operatorComparer.Compare(condition.Operation, currentCulture.Parent.Name, condition.Value); - return result ? True : False; - } + return result ? True : False; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/HomepageConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/HomepageConditionEvaluator.cs index 8a6103cb516..0c96b02ca1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/HomepageConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/HomepageConditionEvaluator.cs @@ -3,29 +3,28 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services -{ - public class HomepageConditionEvaluator : ConditionEvaluator - { - private readonly IHttpContextAccessor _httpContextAccessor; +namespace OrchardCore.Rules.Services; - public HomepageConditionEvaluator(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } +public class HomepageConditionEvaluator : ConditionEvaluator +{ + private readonly IHttpContextAccessor _httpContextAccessor; - public override ValueTask EvaluateAsync(HomepageCondition condition) - { - var requestPath = _httpContextAccessor.HttpContext.Request.Path.Value; + public HomepageConditionEvaluator(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } - var result = string.Equals("/", requestPath, StringComparison.Ordinal) || string.IsNullOrEmpty(requestPath); + public override ValueTask EvaluateAsync(HomepageCondition condition) + { + var requestPath = _httpContextAccessor.HttpContext.Request.Path.Value; - if (!condition.Value) - { - result = !result; - } + var result = string.Equals("/", requestPath, StringComparison.Ordinal) || string.IsNullOrEmpty(requestPath); - return result ? True : False; + if (!condition.Value) + { + result = !result; } + + return result ? True : False; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAnonymousConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAnonymousConditionEvaluator.cs index 8d2234f0c15..9f84707324b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAnonymousConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAnonymousConditionEvaluator.cs @@ -2,18 +2,17 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services -{ - public class IsAnonymousConditionEvaluator : ConditionEvaluator - { - private readonly IHttpContextAccessor _httpContextAccessor; +namespace OrchardCore.Rules.Services; - public IsAnonymousConditionEvaluator(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } +public class IsAnonymousConditionEvaluator : ConditionEvaluator +{ + private readonly IHttpContextAccessor _httpContextAccessor; - public override ValueTask EvaluateAsync(IsAnonymousCondition condition) - => _httpContextAccessor.HttpContext.User?.Identity.IsAuthenticated != true ? True : False; + public IsAnonymousConditionEvaluator(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; } + + public override ValueTask EvaluateAsync(IsAnonymousCondition condition) + => _httpContextAccessor.HttpContext.User?.Identity.IsAuthenticated != true ? True : False; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAuthenticatedConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAuthenticatedConditionEvaluator.cs index d0b147ac393..abcc1d89d6d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAuthenticatedConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/IsAuthenticatedConditionEvaluator.cs @@ -2,18 +2,17 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services -{ - public class IsAuthenticatedConditionEvaluator : ConditionEvaluator - { - private readonly IHttpContextAccessor _httpContextAccessor; +namespace OrchardCore.Rules.Services; - public IsAuthenticatedConditionEvaluator(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } +public class IsAuthenticatedConditionEvaluator : ConditionEvaluator +{ + private readonly IHttpContextAccessor _httpContextAccessor; - public override ValueTask EvaluateAsync(IsAuthenticatedCondition condition) - => _httpContextAccessor.HttpContext.User?.Identity.IsAuthenticated == true ? True : False; + public IsAuthenticatedConditionEvaluator(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; } + + public override ValueTask EvaluateAsync(IsAuthenticatedCondition condition) + => _httpContextAccessor.HttpContext.User?.Identity.IsAuthenticated == true ? True : False; } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/JavascriptConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/JavascriptConditionEvaluator.cs index 44454b42398..e5419c9b02b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/JavascriptConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/JavascriptConditionEvaluator.cs @@ -4,30 +4,29 @@ using OrchardCore.Rules.Models; using OrchardCore.Scripting; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class JavascriptConditionEvaluator : ConditionEvaluator { - public class JavascriptConditionEvaluator : ConditionEvaluator - { - private readonly IScriptingManager _scriptingManager; - private readonly IServiceProvider _serviceProvider; + private readonly IScriptingManager _scriptingManager; + private readonly IServiceProvider _serviceProvider; - // The scope is built lazily once per request. - private IScriptingScope _scope; - private IScriptingEngine _engine; + // The scope is built lazily once per request. + private IScriptingScope _scope; + private IScriptingEngine _engine; - public JavascriptConditionEvaluator(IScriptingManager scriptingManager, IServiceProvider serviceProvider) - { - _scriptingManager = scriptingManager; - _serviceProvider = serviceProvider; - } + public JavascriptConditionEvaluator(IScriptingManager scriptingManager, IServiceProvider serviceProvider) + { + _scriptingManager = scriptingManager; + _serviceProvider = serviceProvider; + } - public override ValueTask EvaluateAsync(JavascriptCondition condition) - { - _engine ??= _scriptingManager.GetScriptingEngine("js"); - _scope ??= _engine.CreateScope(_scriptingManager.GlobalMethodProviders.SelectMany(x => x.GetMethods()), _serviceProvider, null, null); + public override ValueTask EvaluateAsync(JavascriptCondition condition) + { + _engine ??= _scriptingManager.GetScriptingEngine("js"); + _scope ??= _engine.CreateScope(_scriptingManager.GlobalMethodProviders.SelectMany(x => x.GetMethods()), _serviceProvider, null, null); - return Convert.ToBoolean(_engine.Evaluate(_scope, condition.Script)) ? True : False; - } + return Convert.ToBoolean(_engine.Evaluate(_scope, condition.Script)) ? True : False; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/RoleConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/RoleConditionEvaluator.cs index 4636a1b30ce..ba83e92337e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/RoleConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/RoleConditionEvaluator.cs @@ -5,43 +5,42 @@ using Microsoft.Extensions.Options; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services -{ - public class RoleConditionEvaluator : ConditionEvaluator - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IdentityOptions _options; - private readonly IConditionOperatorResolver _operatorResolver; +namespace OrchardCore.Rules.Services; - public RoleConditionEvaluator( - IHttpContextAccessor httpContextAccessor, - IOptions options, - IConditionOperatorResolver operatorResolver) - { - _httpContextAccessor = httpContextAccessor; - _options = options.Value; - _operatorResolver = operatorResolver; - } +public class RoleConditionEvaluator : ConditionEvaluator +{ + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IdentityOptions _options; + private readonly IConditionOperatorResolver _operatorResolver; - public override ValueTask EvaluateAsync(RoleCondition condition) - { - var roleClaimType = _options.ClaimsIdentity.RoleClaimType; + public RoleConditionEvaluator( + IHttpContextAccessor httpContextAccessor, + IOptions options, + IConditionOperatorResolver operatorResolver) + { + _httpContextAccessor = httpContextAccessor; + _options = options.Value; + _operatorResolver = operatorResolver; + } - // IsInRole() & HasClaim() are case sensitive. - var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); + public override ValueTask EvaluateAsync(RoleCondition condition) + { + var roleClaimType = _options.ClaimsIdentity.RoleClaimType; - // Claim all if the operator is negative - if (condition.Operation is INegateOperator) - { - return (_httpContextAccessor.HttpContext.User?.Claims.Where(c => c.Type == roleClaimType).All(claim => - operatorComparer.Compare(condition.Operation, claim.Value, condition.Value)) - ).GetValueOrDefault() ? True : False; - } + // IsInRole() & HasClaim() are case sensitive. + var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); - return (_httpContextAccessor.HttpContext.User?.Claims.Any(claim => - claim.Type == roleClaimType && + // Claim all if the operator is negative + if (condition.Operation is INegateOperator) + { + return (_httpContextAccessor.HttpContext.User?.Claims.Where(c => c.Type == roleClaimType).All(claim => operatorComparer.Compare(condition.Operation, claim.Value, condition.Value)) ).GetValueOrDefault() ? True : False; } + + return (_httpContextAccessor.HttpContext.User?.Claims.Any(claim => + claim.Type == roleClaimType && + operatorComparer.Compare(condition.Operation, claim.Value, condition.Value)) + ).GetValueOrDefault() ? True : False; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/RuleService.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/RuleService.cs index 903a94ead22..0044a40a974 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/RuleService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/RuleService.cs @@ -1,34 +1,33 @@ using System.Threading.Tasks; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class RuleService : IRuleService { - public class RuleService : IRuleService - { - private readonly IConditionResolver _conditionResolver; + private readonly IConditionResolver _conditionResolver; - public RuleService(IConditionResolver conditionResolver) - { - _conditionResolver = conditionResolver; - } + public RuleService(IConditionResolver conditionResolver) + { + _conditionResolver = conditionResolver; + } - public async ValueTask EvaluateAsync(Rule rule) + public async ValueTask EvaluateAsync(Rule rule) + { + foreach (var childCondition in rule.Conditions) { - foreach (var childCondition in rule.Conditions) + var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); + if (evaluator is null || !await evaluator.EvaluateAsync(childCondition)) { - var evaluator = _conditionResolver.GetConditionEvaluator(childCondition); - if (evaluator is null || !await evaluator.EvaluateAsync(childCondition)) - { - return false; - } - } - - if (rule.Conditions.Count > 0) - { - return true; + return false; } + } - // This rule requires all conditions to be evaluated as true. - return false; + if (rule.Conditions.Count > 0) + { + return true; } + + // This rule requires all conditions to be evaluated as true. + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/StringOperatorComparer.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/StringOperatorComparer.cs index ceeeb32be65..39b7b1efe9d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/StringOperatorComparer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/StringOperatorComparer.cs @@ -1,69 +1,68 @@ using System; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class StringEqualsOperatorComparer : OperatorComparer { - public class StringEqualsOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - string.Equals(a, b, StringComparison.Ordinal) : - string.Equals(a, b, StringComparison.OrdinalIgnoreCase); - } + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + string.Equals(a, b, StringComparison.Ordinal) : + string.Equals(a, b, StringComparison.OrdinalIgnoreCase); +} - public class StringNotEqualsOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - !string.Equals(a, b, StringComparison.Ordinal) : - !string.Equals(a, b, StringComparison.OrdinalIgnoreCase); - } +public class StringNotEqualsOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + !string.Equals(a, b, StringComparison.Ordinal) : + !string.Equals(a, b, StringComparison.OrdinalIgnoreCase); +} - public class StringStartsWithOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - a.StartsWith(b) : - a.StartsWith(b, StringComparison.OrdinalIgnoreCase); - } +public class StringStartsWithOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + a.StartsWith(b) : + a.StartsWith(b, StringComparison.OrdinalIgnoreCase); +} - public class StringNotStartsWithOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - !a.StartsWith(b) : - !a.StartsWith(b, StringComparison.OrdinalIgnoreCase); - } +public class StringNotStartsWithOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + !a.StartsWith(b) : + !a.StartsWith(b, StringComparison.OrdinalIgnoreCase); +} - public class StringEndsWithOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - a.EndsWith(b) : - a.EndsWith(b, StringComparison.OrdinalIgnoreCase); - } +public class StringEndsWithOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + a.EndsWith(b) : + a.EndsWith(b, StringComparison.OrdinalIgnoreCase); +} - public class StringNotEndsWithOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - !a.EndsWith(b) : - !a.EndsWith(b, StringComparison.OrdinalIgnoreCase); - } +public class StringNotEndsWithOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + !a.EndsWith(b) : + !a.EndsWith(b, StringComparison.OrdinalIgnoreCase); +} - public class StringContainsOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - a.Contains(b) : - a.Contains(b, StringComparison.OrdinalIgnoreCase); - } +public class StringContainsOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + a.Contains(b) : + a.Contains(b, StringComparison.OrdinalIgnoreCase); +} - public class StringNotContainsOperatorComparer : OperatorComparer - { - public override bool Compare(StringOperator conditionOperator, string a, string b) - => conditionOperator.CaseSensitive ? - !a.Contains(b) : - !a.Contains(b, StringComparison.OrdinalIgnoreCase); - } +public class StringNotContainsOperatorComparer : OperatorComparer +{ + public override bool Compare(StringOperator conditionOperator, string a, string b) + => conditionOperator.CaseSensitive ? + !a.Contains(b) : + !a.Contains(b, StringComparison.OrdinalIgnoreCase); } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Services/UrlConditionEvaluator.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Services/UrlConditionEvaluator.cs index 6d663b3fae1..3f44295ab06 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Services/UrlConditionEvaluator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Services/UrlConditionEvaluator.cs @@ -3,37 +3,36 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.Services +namespace OrchardCore.Rules.Services; + +public class UrlConditionEvaluator : ConditionEvaluator { - public class UrlConditionEvaluator : ConditionEvaluator + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IConditionOperatorResolver _operatorResolver; + + public UrlConditionEvaluator(IHttpContextAccessor httpContextAccessor, IConditionOperatorResolver operatorResolver) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IConditionOperatorResolver _operatorResolver; + _httpContextAccessor = httpContextAccessor; + _operatorResolver = operatorResolver; + } - public UrlConditionEvaluator(IHttpContextAccessor httpContextAccessor, IConditionOperatorResolver operatorResolver) + public override ValueTask EvaluateAsync(UrlCondition condition) + { + if (condition.Value.StartsWith("~/", StringComparison.Ordinal)) { - _httpContextAccessor = httpContextAccessor; - _operatorResolver = operatorResolver; + condition.Value = condition.Value[1..]; } - public override ValueTask EvaluateAsync(UrlCondition condition) - { - if (condition.Value.StartsWith("~/", StringComparison.Ordinal)) - { - condition.Value = condition.Value[1..]; - } + var requestPath = _httpContextAccessor.HttpContext.Request.Path.Value; - var requestPath = _httpContextAccessor.HttpContext.Request.Path.Value; - - // Tenant home page could have an empty string as a request path, where - // the default tenant does not. - if (string.IsNullOrEmpty(requestPath)) - { - requestPath = "/"; - } - - var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); - return operatorComparer.Compare(condition.Operation, requestPath, condition.Value) ? True : False; + // Tenant home page could have an empty string as a request path, where + // the default tenant does not. + if (string.IsNullOrEmpty(requestPath)) + { + requestPath = "/"; } + + var operatorComparer = _operatorResolver.GetOperatorComparer(condition.Operation); + return operatorComparer.Compare(condition.Operation, requestPath, condition.Value) ? True : False; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Startup.cs index 68944d35554..3709db57e0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Startup.cs @@ -7,67 +7,66 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.Services; -namespace OrchardCore.Rules +namespace OrchardCore.Rules; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddOptions(); + services.AddOptions(); - // Rule services. - services - .AddScoped, RuleDisplayDriver>() - .AddSingleton() - .AddTransient, ConditionOperatorConfigureOptions>() - .AddScoped() - .AddScoped() - .AddScoped(); + // Rule services. + services + .AddScoped, RuleDisplayDriver>() + .AddSingleton() + .AddTransient, ConditionOperatorConfigureOptions>() + .AddScoped() + .AddScoped() + .AddScoped(); - // All condition. - services.AddRule(); + // All condition. + services.AddRule(); - // Any condition. - services.AddRule(); + // Any condition. + services.AddRule(); - // Boolean condition. - services.AddRule(); + // Boolean condition. + services.AddRule(); - // Homepage condition. - services.AddRule(); + // Homepage condition. + services.AddRule(); - // Url condition. - services.AddRule(); + // Url condition. + services.AddRule(); - // Culture condition. - services.AddRule(); + // Culture condition. + services.AddRule(); - // Role condition. - services.AddRule(); + // Role condition. + services.AddRule(); - // JavaScript condition. - services.AddRule(); + // JavaScript condition. + services.AddRule(); - // Is authenticated condition. - services.AddRule(); + // Is authenticated condition. + services.AddRule(); - // Is anonymous condition. - services.AddRule(); + // Is anonymous condition. + services.AddRule(); - // Content type condition. - services.AddScoped, ContentTypeConditionDisplayDriver>() - .AddRuleCondition() - .AddScoped(sp => sp.GetRequiredService()); + // Content type condition. + services.AddScoped, ContentTypeConditionDisplayDriver>() + .AddRuleCondition() + .AddScoped(sp => sp.GetRequiredService()); - // Allows to serialize 'ConditionOperator' derived types - services.AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator() - .AddRuleConditionOperator(); - } + // Allows to serialize 'ConditionOperator' derived types + services.AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator() + .AddRuleConditionOperator(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewComponents/SelectStringOperationViewComponent.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewComponents/SelectStringOperationViewComponent.cs index dce865427a9..2f042fd15d2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewComponents/SelectStringOperationViewComponent.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewComponents/SelectStringOperationViewComponent.cs @@ -6,38 +6,37 @@ using OrchardCore.Rules.Models; using OrchardCore.Rules.ViewModels; -namespace OrchardCore.Rules.ViewComponents +namespace OrchardCore.Rules.ViewComponents; + +public class SelectStringOperationViewComponent : ViewComponent { - public class SelectStringOperationViewComponent : ViewComponent - { - private readonly ConditionOperatorOptions _options; - private readonly IServiceProvider _serviceProvider; + private readonly ConditionOperatorOptions _options; + private readonly IServiceProvider _serviceProvider; - public SelectStringOperationViewComponent(IOptions options, IServiceProvider serviceProvider) - { - _options = options.Value; - _serviceProvider = serviceProvider; - } + public SelectStringOperationViewComponent(IOptions options, IServiceProvider serviceProvider) + { + _options = options.Value; + _serviceProvider = serviceProvider; + } - public IViewComponentResult Invoke(string selectedOperation, string htmlName) - { - var stringOperators = _options.Operators.Where(x => typeof(StringOperator).IsAssignableFrom(x.Operator)); + public IViewComponentResult Invoke(string selectedOperation, string htmlName) + { + var stringOperators = _options.Operators.Where(x => typeof(StringOperator).IsAssignableFrom(x.Operator)); - var items = stringOperators - .Select(x => - new SelectListItem( - x.DisplayText(_serviceProvider), - x.Operator.Name, - string.Equals(x.Factory.Name, selectedOperation, StringComparison.OrdinalIgnoreCase)) - ).ToList(); + var items = stringOperators + .Select(x => + new SelectListItem( + x.DisplayText(_serviceProvider), + x.Operator.Name, + string.Equals(x.Factory.Name, selectedOperation, StringComparison.OrdinalIgnoreCase)) + ).ToList(); - var model = new SelectStringOperationViewModel - { - HtmlName = htmlName, - Items = items - }; + var model = new SelectStringOperationViewModel + { + HtmlName = htmlName, + Items = items + }; - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AllConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AllConditionViewModel.cs index 564ba349ba0..8133484678a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AllConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AllConditionViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class AllConditionViewModel { - public class AllConditionViewModel - { - public string DisplayText { get; set; } + public string DisplayText { get; set; } - [BindNever] - public AllConditionGroup Condition { get; set; } - } + [BindNever] + public AllConditionGroup Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AnyConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AnyConditionViewModel.cs index 358707f7095..e774d4d7825 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AnyConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/AnyConditionViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class AnyConditionViewModel { - public class AnyConditionViewModel - { - public string DisplayText { get; set; } + public string DisplayText { get; set; } - [BindNever] - public AnyConditionGroup Condition { get; set; } - } + [BindNever] + public AnyConditionGroup Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/BooleanConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/BooleanConditionViewModel.cs index 72f6f93f0c0..9b00f98c842 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/BooleanConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/BooleanConditionViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class BooleanConditionViewModel { - public class BooleanConditionViewModel - { - public bool Value { get; set; } + public bool Value { get; set; } - [BindNever] - public BooleanCondition Condition { get; set; } - } + [BindNever] + public BooleanCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ConditionGroupViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ConditionGroupViewModel.cs index c739cfe0448..f620ce22b98 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ConditionGroupViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ConditionGroupViewModel.cs @@ -1,18 +1,17 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class ConditionGroupViewModel { - public class ConditionGroupViewModel - { - public ConditionEntry[] Entries { get; set; } + public ConditionEntry[] Entries { get; set; } - [BindNever] - public ConditionGroup Condition { get; set; } - } + [BindNever] + public ConditionGroup Condition { get; set; } +} - public class ConditionEntry - { - [BindNever] - public Condition Condition { get; set; } - } +public class ConditionEntry +{ + [BindNever] + public Condition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ContentTypeConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ContentTypeConditionViewModel.cs index 2650b76a2ef..c6fc60122aa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ContentTypeConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/ContentTypeConditionViewModel.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class ContentTypeConditionViewModel { - public class ContentTypeConditionViewModel - { - public string SelectedOperation { get; set; } - public string Value { get; set; } + public string SelectedOperation { get; set; } + public string Value { get; set; } - [BindNever] - public ContentTypeCondition Condition { get; set; } - } + [BindNever] + public ContentTypeCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/CultureConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/CultureConditionViewModel.cs index 8a7e874cc8d..9ea6482765d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/CultureConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/CultureConditionViewModel.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class CultureConditionViewModel { - public class CultureConditionViewModel - { - public string SelectedOperation { get; set; } - public string Value { get; set; } + public string SelectedOperation { get; set; } + public string Value { get; set; } - [BindNever] - public CultureCondition Condition { get; set; } - } + [BindNever] + public CultureCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/HomepageConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/HomepageConditionViewModel.cs index a6a93f79790..f1966d52d7d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/HomepageConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/HomepageConditionViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class HomepageConditionViewModel { - public class HomepageConditionViewModel - { - public bool Value { get; set; } + public bool Value { get; set; } - [BindNever] - public HomepageCondition Condition { get; set; } - } + [BindNever] + public HomepageCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/JavascriptConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/JavascriptConditionViewModel.cs index 6427824aeaa..fdeebc37600 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/JavascriptConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/JavascriptConditionViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class JavascriptConditionViewModel { - public class JavascriptConditionViewModel - { - public string Script { get; set; } + public string Script { get; set; } - [BindNever] - public JavascriptCondition Condition { get; set; } - } + [BindNever] + public JavascriptCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/RoleConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/RoleConditionViewModel.cs index fe192c3e8e9..2b52cc228db 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/RoleConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/RoleConditionViewModel.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class RoleConditionViewModel { - public class RoleConditionViewModel - { - public string SelectedOperation { get; set; } - public string Value { get; set; } + public string SelectedOperation { get; set; } + public string Value { get; set; } - [BindNever] - public RoleCondition Condition { get; set; } - } + [BindNever] + public RoleCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/SelectStringOperationViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/SelectStringOperationViewModel.cs index 48d2fe77585..f3eed12023f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/SelectStringOperationViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/SelectStringOperationViewModel.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class SelectStringOperationViewModel { - public class SelectStringOperationViewModel - { - public string HtmlName { get; set; } - public List Items { get; set; } - } + public string HtmlName { get; set; } + public List Items { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/UrlConditionViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/UrlConditionViewModel.cs index ab476402ece..7e19ee8e0ba 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/UrlConditionViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/ViewModels/UrlConditionViewModel.cs @@ -1,14 +1,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; -namespace OrchardCore.Rules.ViewModels +namespace OrchardCore.Rules.ViewModels; + +public class UrlConditionViewModel { - public class UrlConditionViewModel - { - public string SelectedOperation { get; set; } - public string Value { get; set; } + public string SelectedOperation { get; set; } + public string Value { get; set; } - [BindNever] - public UrlCondition Condition { get; set; } - } + [BindNever] + public UrlCondition Condition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Scripting/Providers/LogProvider.cs b/src/OrchardCore.Modules/OrchardCore.Scripting/Providers/LogProvider.cs index b95873d35c8..f3ab73cd30a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Scripting/Providers/LogProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Scripting/Providers/LogProvider.cs @@ -2,59 +2,58 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; -namespace OrchardCore.Scripting.Providers +namespace OrchardCore.Scripting.Providers; + +public class LogProvider : IGlobalMethodProvider { - public class LogProvider : IGlobalMethodProvider - { - private readonly GlobalMethod _log; + private readonly GlobalMethod _log; - public LogProvider(ILogger logger) + public LogProvider(ILogger logger) + { + _log = new GlobalMethod { - _log = new GlobalMethod + Name = "log", + Method = serviceProvider => (Action)((level, text, param) => { - Name = "log", - Method = serviceProvider => (Action)((level, text, param) => + try { - try + if (!Enum.TryParse(level, true, out var logLevel)) + { + logLevel = LogLevel.Information; + } + if (param == null) { - if (!Enum.TryParse(level, true, out var logLevel)) - { - logLevel = LogLevel.Information; - } - if (param == null) - { #pragma warning disable CA2254 // Template should be a static expression - logger.Log(logLevel, text); + logger.Log(logLevel, text); #pragma warning restore CA2254 // Template should be a static expression + } + else + { + object[] args; + if (param is not Array) + { + args = [param]; } else { - object[] args; - if (param is not Array) - { - args = [param]; - } - else - { - args = (object[])param; - } + args = (object[])param; + } #pragma warning disable CA2254 // Template should be a static expression - logger.Log(logLevel, text, args); + logger.Log(logLevel, text, args); #pragma warning restore CA2254 // Template should be a static expression - } - } - catch (Exception ex) - { - logger.Log(LogLevel.Error, ex, "Error logging text template {text} with param {param} from Scripting Engine.", text, param); } - }), - }; - } + } + catch (Exception ex) + { + logger.Log(LogLevel.Error, ex, "Error logging text template {text} with param {param} from Scripting Engine.", text, param); + } + }), + }; + } - public IEnumerable GetMethods() - { - return new[] { _log }; - } + public IEnumerable GetMethods() + { + return new[] { _log }; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Scripting/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Scripting/Startup.cs index 79bd02d6f3c..46d66032d53 100644 --- a/src/OrchardCore.Modules/OrchardCore.Scripting/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Scripting/Startup.cs @@ -3,14 +3,13 @@ using OrchardCore.Scripting.JavaScript; using OrchardCore.Scripting.Providers; -namespace OrchardCore.Scripting +namespace OrchardCore.Scripting; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddJavaScriptEngine(); - services.AddSingleton(); - } + services.AddJavaScriptEngine(); + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/AdminController.cs index 28f1b3ff25c..5e270e43dfe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/AdminController.cs @@ -33,620 +33,619 @@ using OrchardCore.Settings; using YesSql; -namespace OrchardCore.Search.Elasticsearch +namespace OrchardCore.Search.Elasticsearch; + +[Admin("elasticsearch/{action}/{id?}", "Elasticsearch.{action}")] +public class AdminController : Controller { - [Admin("elasticsearch/{action}/{id?}", "Elasticsearch.{action}")] - public class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly ISession _session; + private readonly ISiteService _siteService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IAuthorizationService _authorizationService; + private readonly IElasticQueryService _queryService; + private readonly ElasticIndexManager _elasticIndexManager; + private readonly ElasticIndexingService _elasticIndexingService; + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + private readonly JavaScriptEncoder _javaScriptEncoder; + private readonly ElasticsearchOptions _elasticSearchOptions; + private readonly INotifier _notifier; + private readonly ILogger _logger; + private readonly IOptions _templateOptions; + private readonly ElasticConnectionOptions _elasticConnectionOptions; + private readonly IShapeFactory _shapeFactory; + private readonly ILocalizationService _localizationService; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + ISession session, + ISiteService siteService, + ILiquidTemplateManager liquidTemplateManager, + IContentDefinitionManager contentDefinitionManager, + IAuthorizationService authorizationService, + IElasticQueryService queryService, + ElasticIndexManager elasticIndexManager, + ElasticIndexingService elasticIndexingService, + ElasticIndexSettingsService elasticIndexSettingsService, + JavaScriptEncoder javaScriptEncoder, + IOptions elasticSearchOptions, + INotifier notifier, + ILogger logger, + IOptions templateOptions, + IOptions elasticConnectionOptions, + IShapeFactory shapeFactory, + ILocalizationService localizationService, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) { - private const string _optionsSearch = "Options.Search"; - - private readonly ISession _session; - private readonly ISiteService _siteService; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IAuthorizationService _authorizationService; - private readonly IElasticQueryService _queryService; - private readonly ElasticIndexManager _elasticIndexManager; - private readonly ElasticIndexingService _elasticIndexingService; - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; - private readonly JavaScriptEncoder _javaScriptEncoder; - private readonly ElasticsearchOptions _elasticSearchOptions; - private readonly INotifier _notifier; - private readonly ILogger _logger; - private readonly IOptions _templateOptions; - private readonly ElasticConnectionOptions _elasticConnectionOptions; - private readonly IShapeFactory _shapeFactory; - private readonly ILocalizationService _localizationService; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - ISession session, - ISiteService siteService, - ILiquidTemplateManager liquidTemplateManager, - IContentDefinitionManager contentDefinitionManager, - IAuthorizationService authorizationService, - IElasticQueryService queryService, - ElasticIndexManager elasticIndexManager, - ElasticIndexingService elasticIndexingService, - ElasticIndexSettingsService elasticIndexSettingsService, - JavaScriptEncoder javaScriptEncoder, - IOptions elasticSearchOptions, - INotifier notifier, - ILogger logger, - IOptions templateOptions, - IOptions elasticConnectionOptions, - IShapeFactory shapeFactory, - ILocalizationService localizationService, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) - { - _session = session; - _siteService = siteService; - _liquidTemplateManager = liquidTemplateManager; - _contentDefinitionManager = contentDefinitionManager; - _authorizationService = authorizationService; - _queryService = queryService; - _elasticIndexManager = elasticIndexManager; - _elasticIndexingService = elasticIndexingService; - _elasticIndexSettingsService = elasticIndexSettingsService; - _javaScriptEncoder = javaScriptEncoder; - _elasticSearchOptions = elasticSearchOptions.Value; - _notifier = notifier; - _logger = logger; - _templateOptions = templateOptions; - _elasticConnectionOptions = elasticConnectionOptions.Value; - _shapeFactory = shapeFactory; - _localizationService = localizationService; - S = stringLocalizer; - H = htmlLocalizer; - } - - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + _session = session; + _siteService = siteService; + _liquidTemplateManager = liquidTemplateManager; + _contentDefinitionManager = contentDefinitionManager; + _authorizationService = authorizationService; + _queryService = queryService; + _elasticIndexManager = elasticIndexManager; + _elasticIndexingService = elasticIndexingService; + _elasticIndexSettingsService = elasticIndexSettingsService; + _javaScriptEncoder = javaScriptEncoder; + _elasticSearchOptions = elasticSearchOptions.Value; + _notifier = notifier; + _logger = logger; + _templateOptions = templateOptions; + _elasticConnectionOptions = elasticConnectionOptions.Value; + _shapeFactory = shapeFactory; + _localizationService = localizationService; + S = stringLocalizer; + H = htmlLocalizer; + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return NotConfigured(); - } + public async Task Index(ContentOptions options, PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); + } - var indexes = (await _elasticIndexSettingsService.GetSettingsAsync()) - .Select(i => new IndexViewModel { Name = i.IndexName }) - .ToList(); + if (!_elasticConnectionOptions.FileConfigurationExists()) + { + return NotConfigured(); + } - var totalIndexes = indexes.Count; - var siteSettings = await _siteService.GetSiteSettingsAsync(); - var pager = new Pager(pagerParameters, siteSettings.PageSize); + var indexes = (await _elasticIndexSettingsService.GetSettingsAsync()) + .Select(i => new IndexViewModel { Name = i.IndexName }) + .ToList(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - indexes = indexes.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + var totalIndexes = indexes.Count; + var siteSettings = await _siteService.GetSiteSettingsAsync(); + var pager = new Pager(pagerParameters, siteSettings.PageSize); - indexes = indexes - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); + if (!string.IsNullOrWhiteSpace(options.Search)) + { + indexes = indexes.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + indexes = indexes + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ToList(); - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - var pagerShape = await _shapeFactory.PagerAsync(pager, totalIndexes, routeData); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - var model = new AdminIndexViewModel - { - Indexes = indexes, - Options = options, - Pager = pagerShape - }; + var pagerShape = await _shapeFactory.PagerAsync(pager, totalIndexes, routeData); - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Reset"], nameof(ContentsBulkAction.Reset)), - new SelectListItem(S["Rebuild"], nameof(ContentsBulkAction.Rebuild)), - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + var model = new AdminIndexViewModel + { + Indexes = indexes, + Options = options, + Pager = pagerShape + }; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Reset"], nameof(ContentsBulkAction.Reset)), + new SelectListItem(S["Rebuild"], nameof(ContentsBulkAction.Rebuild)), + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - return View(model); - } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public IActionResult IndexFilterPOST(AdminIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + return View(model); + } - public async Task Edit(string indexName = null) + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public IActionResult IndexFilterPOST(AdminIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary { - var IsCreate = string.IsNullOrWhiteSpace(indexName); - var settings = new ElasticIndexSettings(); + { _optionsSearch, model.Options.Search } + }); - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + public async Task Edit(string indexName = null) + { + var IsCreate = string.IsNullOrWhiteSpace(indexName); + var settings = new ElasticIndexSettings(); - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return NotConfigured(); - } + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); + } - if (!IsCreate) - { - settings = await _elasticIndexSettingsService.GetSettingsAsync(indexName); + if (!_elasticConnectionOptions.FileConfigurationExists()) + { + return NotConfigured(); + } - if (settings == null) - { - return NotFound(); - } - } + if (!IsCreate) + { + settings = await _elasticIndexSettingsService.GetSettingsAsync(indexName); - var model = new ElasticIndexSettingsViewModel + if (settings == null) { - IsCreate = IsCreate, - IndexName = IsCreate ? string.Empty : settings.IndexName, - AnalyzerName = IsCreate ? "standardanalyzer" : settings.AnalyzerName, - IndexLatest = settings.IndexLatest, - Culture = settings.Culture, - IndexedContentTypes = settings.IndexedContentTypes, - StoreSourceData = settings.StoreSourceData - }; + return NotFound(); + } + } - await PopulateMenuOptionsAsync(model); + var model = new ElasticIndexSettingsViewModel + { + IsCreate = IsCreate, + IndexName = IsCreate ? string.Empty : settings.IndexName, + AnalyzerName = IsCreate ? "standardanalyzer" : settings.AnalyzerName, + IndexLatest = settings.IndexLatest, + Culture = settings.Culture, + IndexedContentTypes = settings.IndexedContentTypes, + StoreSourceData = settings.StoreSourceData + }; + + await PopulateMenuOptionsAsync(model); + + return View(model); + } - return View(model); + [HttpPost, ActionName(nameof(Edit))] + public async Task EditPost(ElasticIndexSettingsViewModel model, string[] indexedContentTypes) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Edit))] - public async Task EditPost(ElasticIndexSettingsViewModel model, string[] indexedContentTypes) + if (!_elasticConnectionOptions.FileConfigurationExists()) + { + return BadRequest(); + } + + ValidateModel(model); + + if (model.IsCreate) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + if (await _elasticIndexManager.ExistsAsync(model.IndexName)) { - return Forbid(); + ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["An index named {0} already exists.", model.IndexName]); } - - if (!_elasticConnectionOptions.FileConfigurationExists()) + } + else + { + if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) { - return BadRequest(); + ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["An index named {0} doesn't exist.", model.IndexName]); } + } + + if (!ModelState.IsValid) + { + await PopulateMenuOptionsAsync(model); - ValidateModel(model); + return View(model); + } - if (model.IsCreate) + if (model.IsCreate) + { + try { - if (await _elasticIndexManager.ExistsAsync(model.IndexName)) + var settings = new ElasticIndexSettings { - ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["An index named {0} already exists.", model.IndexName]); - } + IndexName = model.IndexName, + AnalyzerName = model.AnalyzerName, + QueryAnalyzerName = model.AnalyzerName, + IndexLatest = model.IndexLatest, + IndexedContentTypes = indexedContentTypes, + Culture = model.Culture ?? string.Empty, + StoreSourceData = model.StoreSourceData + }; + + // We call Rebuild in order to reset the index state cursor too in case the same index + // name was also used previously. + await _elasticIndexingService.CreateIndexAsync(settings); } - else + catch (Exception e) { - if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) - { - ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["An index named {0} doesn't exist.", model.IndexName]); - } - } + await _notifier.ErrorAsync(H["An error occurred while creating the index."]); + _logger.LogError(e, "An error occurred while creating index: {indexName}.", _elasticIndexManager.GetFullIndexName(model.IndexName)); - if (!ModelState.IsValid) - { await PopulateMenuOptionsAsync(model); return View(model); } - if (model.IsCreate) + await _notifier.SuccessAsync(H["Index {0} created successfully.", model.IndexName]); + } + else + { + try { - try - { - var settings = new ElasticIndexSettings - { - IndexName = model.IndexName, - AnalyzerName = model.AnalyzerName, - QueryAnalyzerName = model.AnalyzerName, - IndexLatest = model.IndexLatest, - IndexedContentTypes = indexedContentTypes, - Culture = model.Culture ?? string.Empty, - StoreSourceData = model.StoreSourceData - }; - - // We call Rebuild in order to reset the index state cursor too in case the same index - // name was also used previously. - await _elasticIndexingService.CreateIndexAsync(settings); - } - catch (Exception e) + var settings = new ElasticIndexSettings { - await _notifier.ErrorAsync(H["An error occurred while creating the index."]); - _logger.LogError(e, "An error occurred while creating index: {indexName}.", _elasticIndexManager.GetFullIndexName(model.IndexName)); + IndexName = model.IndexName, + AnalyzerName = model.AnalyzerName, + IndexLatest = model.IndexLatest, + IndexedContentTypes = indexedContentTypes, + Culture = model.Culture ?? string.Empty, + StoreSourceData = model.StoreSourceData + }; - await PopulateMenuOptionsAsync(model); - - return View(model); - } - - await _notifier.SuccessAsync(H["Index {0} created successfully.", model.IndexName]); + await _elasticIndexingService.UpdateIndexAsync(settings); } - else + catch (Exception e) { - try - { - var settings = new ElasticIndexSettings - { - IndexName = model.IndexName, - AnalyzerName = model.AnalyzerName, - IndexLatest = model.IndexLatest, - IndexedContentTypes = indexedContentTypes, - Culture = model.Culture ?? string.Empty, - StoreSourceData = model.StoreSourceData - }; - - await _elasticIndexingService.UpdateIndexAsync(settings); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while editing the index."]); - _logger.LogError(e, "An error occurred while editing index: {indexName}.", _elasticIndexManager.GetFullIndexName(model.IndexName)); + await _notifier.ErrorAsync(H["An error occurred while editing the index."]); + _logger.LogError(e, "An error occurred while editing index: {indexName}.", _elasticIndexManager.GetFullIndexName(model.IndexName)); - await PopulateMenuOptionsAsync(model); - - return View(model); - } + await PopulateMenuOptionsAsync(model); - await _notifier.SuccessAsync(H["Index {0} modified successfully, please consider rebuilding the index.", model.IndexName]); + return View(model); } - return RedirectToAction(nameof(Index)); + await _notifier.SuccessAsync(H["Index {0} modified successfully, please consider rebuilding the index.", model.IndexName]); } - [HttpPost] - public async Task Reset(string id) + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + public async Task Reset(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + return Forbid(); + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + if (!_elasticConnectionOptions.FileConfigurationExists()) + { + return BadRequest(); + } - if (!await _elasticIndexManager.ExistsAsync(id)) - { - return NotFound(); - } + if (!await _elasticIndexManager.ExistsAsync(id)) + { + return NotFound(); + } - await _elasticIndexingService.ResetIndexAsync(id); - await ProcessContentItemsAsync(id); + await _elasticIndexingService.ResetIndexAsync(id); + await ProcessContentItemsAsync(id); - await _notifier.SuccessAsync(H["Index {0} reset successfully.", id]); + await _notifier.SuccessAsync(H["Index {0} reset successfully.", id]); - return RedirectToAction("Index"); + return RedirectToAction("Index"); + } + + [HttpPost] + public async Task Rebuild(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost] - public async Task Rebuild(string id) + if (!_elasticConnectionOptions.FileConfigurationExists()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + return BadRequest(); + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + if (!await _elasticIndexManager.ExistsAsync(id)) + { + return NotFound(); + } - if (!await _elasticIndexManager.ExistsAsync(id)) - { - return NotFound(); - } + var settings = await _elasticIndexSettingsService.GetSettingsAsync(id); - var settings = await _elasticIndexSettingsService.GetSettingsAsync(id); + await _elasticIndexingService.RebuildIndexAsync(settings); - await _elasticIndexingService.RebuildIndexAsync(settings); + if (settings.QueryAnalyzerName != settings.AnalyzerName) + { + // Query Analyzer may be different until the index in rebuilt. + // Since the index is rebuilt, lets make sure we query using the same analyzer. + settings.QueryAnalyzerName = settings.AnalyzerName; - if (settings.QueryAnalyzerName != settings.AnalyzerName) - { - // Query Analyzer may be different until the index in rebuilt. - // Since the index is rebuilt, lets make sure we query using the same analyzer. - settings.QueryAnalyzerName = settings.AnalyzerName; + await _elasticIndexSettingsService.UpdateIndexAsync(settings); + } - await _elasticIndexSettingsService.UpdateIndexAsync(settings); - } + await ProcessContentItemsAsync(id); - await ProcessContentItemsAsync(id); + await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", id]); - await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", id]); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + [HttpPost] + public async Task Delete(ElasticIndexSettingsViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(ElasticIndexSettingsViewModel model) + if (!_elasticConnectionOptions.FileConfigurationExists()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + return BadRequest(); + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) + { + await _notifier.SuccessAsync(H["Index not found on Elasticsearch server.", model.IndexName]); + return RedirectToAction("Index"); + } - if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) - { - await _notifier.SuccessAsync(H["Index not found on Elasticsearch server.", model.IndexName]); - return RedirectToAction("Index"); - } + try + { + await _elasticIndexingService.DeleteIndexAsync(model.IndexName); - try - { - await _elasticIndexingService.DeleteIndexAsync(model.IndexName); + await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); + } + catch (Exception e) + { + await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); + _logger.LogError(e, "An error occurred while deleting the index {indexName}", _elasticIndexManager.GetFullIndexName(model.IndexName)); + } - await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); - _logger.LogError(e, "An error occurred while deleting the index {indexName}", _elasticIndexManager.GetFullIndexName(model.IndexName)); - } + return RedirectToAction("Index"); + } - return RedirectToAction("Index"); + [HttpPost] + public async Task ForceDelete(ElasticIndexSettingsViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost] - public async Task ForceDelete(ElasticIndexSettingsViewModel model) + if (!_elasticConnectionOptions.FileConfigurationExists()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + return BadRequest(); + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + try + { + await _elasticIndexingService.DeleteIndexAsync(model.IndexName); - try - { - await _elasticIndexingService.DeleteIndexAsync(model.IndexName); + await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); + } + catch (Exception e) + { + await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); + _logger.LogError(e, "An error occurred while deleting the index {indexName}", _elasticIndexManager.GetFullIndexName(model.IndexName)); + } - await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); - _logger.LogError(e, "An error occurred while deleting the index {indexName}", _elasticIndexManager.GetFullIndexName(model.IndexName)); - } + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); - } + public async Task Mappings(string indexName) + { + var mappings = await _elasticIndexManager.GetIndexMappings(indexName); + var formattedJson = JNode.Parse(mappings).ToJsonString(JOptions.Indented); + return View(new MappingsViewModel + { + IndexName = _elasticIndexManager.GetFullIndexName(indexName), + Mappings = formattedJson + }); + } - public async Task Mappings(string indexName) + public async Task SyncSettings() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) { - var mappings = await _elasticIndexManager.GetIndexMappings(indexName); - var formattedJson = JNode.Parse(mappings).ToJsonString(JOptions.Indented); - return View(new MappingsViewModel - { - IndexName = _elasticIndexManager.GetFullIndexName(indexName), - Mappings = formattedJson - }); + return Forbid(); } - public async Task SyncSettings() - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + await _elasticIndexingService.SyncSettings(); - await _elasticIndexingService.SyncSettings(); + return RedirectToAction(nameof(Index)); + } - return RedirectToAction(nameof(Index)); + public async Task Query(string indexName, string query) + { + if (!_elasticConnectionOptions.FileConfigurationExists()) + { + return NotConfigured(); } - public async Task Query(string indexName, string query) + return await Query(new AdminQueryViewModel { - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return NotConfigured(); - } + IndexName = indexName, + DecodedQuery = string.IsNullOrWhiteSpace(query) ? string.Empty : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)) + }); + } - return await Query(new AdminQueryViewModel - { - IndexName = indexName, - DecodedQuery = string.IsNullOrWhiteSpace(query) ? string.Empty : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)) - }); + [HttpPost] + public async Task Query(AdminQueryViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost] - public async Task Query(AdminQueryViewModel model) + if (!_elasticConnectionOptions.FileConfigurationExists()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } - - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + return BadRequest(); + } - model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - // Can't query if there are no indices. - if (model.Indices.Length == 0) - { - return RedirectToAction(nameof(Index)); - } + // Can't query if there are no indices. + if (model.Indices.Length == 0) + { + return RedirectToAction(nameof(Index)); + } - if (string.IsNullOrEmpty(model.IndexName)) - { - model.IndexName = model.Indices[0]; - } + if (string.IsNullOrEmpty(model.IndexName)) + { + model.IndexName = model.Indices[0]; + } - if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) - { - return NotFound(); - } + if (!await _elasticIndexManager.ExistsAsync(model.IndexName)) + { + return NotFound(); + } - if (string.IsNullOrWhiteSpace(model.DecodedQuery)) - { - return View(model); - } + if (string.IsNullOrWhiteSpace(model.DecodedQuery)) + { + return View(model); + } - if (string.IsNullOrEmpty(model.Parameters)) - { - model.Parameters = "{ }"; - } + if (string.IsNullOrEmpty(model.Parameters)) + { + model.Parameters = "{ }"; + } - var stopwatch = new Stopwatch(); - stopwatch.Start(); + var stopwatch = new Stopwatch(); + stopwatch.Start(); - var parameters = JConvert.DeserializeObject>(model.Parameters); - var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions.Value)))); + var parameters = JConvert.DeserializeObject>(model.Parameters); + var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions.Value)))); - try - { - var elasticTopDocs = await _queryService.SearchAsync(model.IndexName, tokenizedContent); + try + { + var elasticTopDocs = await _queryService.SearchAsync(model.IndexName, tokenizedContent); - if (elasticTopDocs != null) - { - model.Documents = elasticTopDocs.TopDocs.Where(x => x != null); - model.Fields = elasticTopDocs.Fields; - model.Count = elasticTopDocs.Count; - } - } - catch (Exception e) + if (elasticTopDocs != null) { - _logger.LogError(e, "Error while executing query"); - ModelState.AddModelError(nameof(model.DecodedQuery), S["Invalid query : {0}", e.Message]); + model.Documents = elasticTopDocs.TopDocs.Where(x => x != null); + model.Fields = elasticTopDocs.Fields; + model.Count = elasticTopDocs.Count; } + } + catch (Exception e) + { + _logger.LogError(e, "Error while executing query"); + ModelState.AddModelError(nameof(model.DecodedQuery), S["Invalid query : {0}", e.Message]); + } - stopwatch.Stop(); - model.Elapsed = stopwatch.Elapsed; - return View(model); + stopwatch.Stop(); + model.Elapsed = stopwatch.Elapsed; + return View(model); + } + + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + if (!_elasticConnectionOptions.FileConfigurationExists()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageElasticIndexes)) - { - return Forbid(); - } + return BadRequest(); + } - if (!_elasticConnectionOptions.FileConfigurationExists()) - { - return BadRequest(); - } + if (itemIds?.Count() > 0) + { + var elasticIndexSettings = await _elasticIndexSettingsService.GetSettingsAsync(); + var checkedContentItems = elasticIndexSettings.Where(x => itemIds.Contains(x.IndexName)); - if (itemIds?.Count() > 0) + switch (options.BulkAction) { - var elasticIndexSettings = await _elasticIndexSettingsService.GetSettingsAsync(); - var checkedContentItems = elasticIndexSettings.Where(x => itemIds.Contains(x.IndexName)); - - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _elasticIndexingService.DeleteIndexAsync(item.IndexName); + } + await _notifier.SuccessAsync(H["Indices successfully removed."]); + break; + case ContentsBulkAction.Reset: + foreach (var item in checkedContentItems) + { + if (!await _elasticIndexManager.ExistsAsync(item.IndexName)) { - await _elasticIndexingService.DeleteIndexAsync(item.IndexName); + return NotFound(); } - await _notifier.SuccessAsync(H["Indices successfully removed."]); - break; - case ContentsBulkAction.Reset: - foreach (var item in checkedContentItems) - { - if (!await _elasticIndexManager.ExistsAsync(item.IndexName)) - { - return NotFound(); - } - await _elasticIndexingService.ResetIndexAsync(item.IndexName); - await ProcessContentItemsAsync(item.IndexName); + await _elasticIndexingService.ResetIndexAsync(item.IndexName); + await ProcessContentItemsAsync(item.IndexName); - await _notifier.SuccessAsync(H["Index {0} reset successfully.", item.IndexName]); - } - break; - case ContentsBulkAction.Rebuild: - foreach (var item in checkedContentItems) + await _notifier.SuccessAsync(H["Index {0} reset successfully.", item.IndexName]); + } + break; + case ContentsBulkAction.Rebuild: + foreach (var item in checkedContentItems) + { + if (!await _elasticIndexManager.ExistsAsync(item.IndexName)) { - if (!await _elasticIndexManager.ExistsAsync(item.IndexName)) - { - return NotFound(); - } + return NotFound(); + } - await _elasticIndexingService.RebuildIndexAsync(await _elasticIndexSettingsService.GetSettingsAsync(item.IndexName)); + await _elasticIndexingService.RebuildIndexAsync(await _elasticIndexSettingsService.GetSettingsAsync(item.IndexName)); - await ProcessContentItemsAsync(item.IndexName); - await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", item.IndexName]); - } - break; - default: - return BadRequest(); - } + await ProcessContentItemsAsync(item.IndexName); + await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", item.IndexName]); + } + break; + default: + return BadRequest(); } - - return RedirectToAction(nameof(Index)); } - private void ValidateModel(ElasticIndexSettingsViewModel model) - { - if (model.IndexedContentTypes == null || model.IndexedContentTypes.Length < 1) - { - ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexedContentTypes), S["At least one content type is required."]); - } + return RedirectToAction(nameof(Index)); + } - if (string.IsNullOrWhiteSpace(model.IndexName)) - { - ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["The index name is required."]); - } - else if (ElasticIndexManager.ToSafeIndexName(model.IndexName) != model.IndexName) - { - ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["The index name contains forbidden characters."]); - } + private void ValidateModel(ElasticIndexSettingsViewModel model) + { + if (model.IndexedContentTypes == null || model.IndexedContentTypes.Length < 1) + { + ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexedContentTypes), S["At least one content type is required."]); } - private async Task PopulateMenuOptionsAsync(ElasticIndexSettingsViewModel model) + if (string.IsNullOrWhiteSpace(model.IndexName)) { - var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); - - model.Cultures = supportedCultures.Select(c => new SelectListItem - { - Text = $"{c} ({CultureInfo.GetCultureInfo(c).DisplayName})", - Value = c - }); - - model.Analyzers = _elasticSearchOptions.Analyzers - .Select(x => new SelectListItem { Text = x.Key, Value = x.Key }); + ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["The index name is required."]); + } + else if (ElasticIndexManager.ToSafeIndexName(model.IndexName) != model.IndexName) + { + ModelState.AddModelError(nameof(ElasticIndexSettingsViewModel.IndexName), S["The index name contains forbidden characters."]); } + } - private ViewResult NotConfigured() - => View("NotConfigured"); + private async Task PopulateMenuOptionsAsync(ElasticIndexSettingsViewModel model) + { + var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); - private static Task ProcessContentItemsAsync(string indexName) - => HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("sync-content-items-elasticsearch-" + indexName, async (scope) => - { - var indexingService = scope.ServiceProvider.GetRequiredService(); - await indexingService.ProcessContentItemsAsync(indexName); - }); + model.Cultures = supportedCultures.Select(c => new SelectListItem + { + Text = $"{c} ({CultureInfo.GetCultureInfo(c).DisplayName})", + Value = c + }); + + model.Analyzers = _elasticSearchOptions.Analyzers + .Select(x => new SelectListItem { Text = x.Key, Value = x.Key }); } + + private ViewResult NotConfigured() + => View("NotConfigured"); + + private static Task ProcessContentItemsAsync(string indexName) + => HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("sync-content-items-elasticsearch-" + indexName, async (scope) => + { + var indexingService = scope.ServiceProvider.GetRequiredService(); + await indexingService.ProcessContentItemsAsync(indexName); + }); } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/ElasticsearchApiController.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/ElasticsearchApiController.cs index a37ce6da0ce..1f61db0906e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/ElasticsearchApiController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Controllers/ElasticsearchApiController.cs @@ -9,98 +9,97 @@ using OrchardCore.Search.Elasticsearch.Models; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch +namespace OrchardCore.Search.Elasticsearch; + +[Route("api/elasticsearch")] +[ApiController] +[Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] +public sealed class ElasticsearchApiController : ControllerBase { - [Route("api/elasticsearch")] - [ApiController] - [Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] - public sealed class ElasticsearchApiController : ControllerBase + private readonly IAuthorizationService _authorizationService; + private readonly IQueryManager _queryManager; + + public ElasticsearchApiController( + IAuthorizationService authorizationService, + IQueryManager queryManager) { - private readonly IAuthorizationService _authorizationService; - private readonly IQueryManager _queryManager; + _authorizationService = authorizationService; + _queryManager = queryManager; + } - public ElasticsearchApiController( - IAuthorizationService authorizationService, - IQueryManager queryManager) + [HttpGet] + [Route("content")] + public async Task Content([FromQuery] ElasticApiQueryViewModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) { - _authorizationService = authorizationService; - _queryManager = queryManager; + return this.ChallengeOrForbid("Api"); } - [HttpGet] - [Route("content")] - public async Task Content([FromQuery] ElasticApiQueryViewModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) - { - return this.ChallengeOrForbid("Api"); - } + var result = await ElasticQueryApiAsync(queryModel, returnContentItems: true); - var result = await ElasticQueryApiAsync(queryModel, returnContentItems: true); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpPost] + [Route("content")] + public async Task ContentPost(ElasticApiQueryViewModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) + { + return this.ChallengeOrForbid(); } - [HttpPost] - [Route("content")] - public async Task ContentPost(ElasticApiQueryViewModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) - { - return this.ChallengeOrForbid(); - } + var result = await ElasticQueryApiAsync(queryModel, returnContentItems: true); - var result = await ElasticQueryApiAsync(queryModel, returnContentItems: true); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpGet] + [Route("documents")] + public async Task Documents([FromQuery] ElasticApiQueryViewModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) + { + return this.ChallengeOrForbid(); } - [HttpGet] - [Route("documents")] - public async Task Documents([FromQuery] ElasticApiQueryViewModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) - { - return this.ChallengeOrForbid(); - } + var result = await ElasticQueryApiAsync(queryModel); - var result = await ElasticQueryApiAsync(queryModel); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpPost] + [Route("documents")] + public async Task DocumentsPost(ElasticApiQueryViewModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) + { + return this.ChallengeOrForbid("Api"); } - [HttpPost] - [Route("documents")] - public async Task DocumentsPost(ElasticApiQueryViewModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryElasticApi)) - { - return this.ChallengeOrForbid("Api"); - } + var result = await ElasticQueryApiAsync(queryModel); - var result = await ElasticQueryApiAsync(queryModel); + return new ObjectResult(result); + } - return new ObjectResult(result); - } + private async Task ElasticQueryApiAsync(ElasticApiQueryViewModel queryModel, bool returnContentItems = false) + { + var elasticQuery = await _queryManager.NewAsync(ElasticQuerySource.SourceName); + elasticQuery.ReturnContentItems = returnContentItems; - private async Task ElasticQueryApiAsync(ElasticApiQueryViewModel queryModel, bool returnContentItems = false) + elasticQuery.Put(new ElasticsearchQueryMetadata { - var elasticQuery = await _queryManager.NewAsync(ElasticQuerySource.SourceName); - elasticQuery.ReturnContentItems = returnContentItems; + Index = queryModel.IndexName, + Template = queryModel.Query, + }); - elasticQuery.Put(new ElasticsearchQueryMetadata - { - Index = queryModel.IndexName, - Template = queryModel.Query, - }); + var queryParameters = queryModel.Parameters != null + ? JConvert.DeserializeObject>(queryModel.Parameters) + : []; - var queryParameters = queryModel.Parameters != null - ? JConvert.DeserializeObject>(queryModel.Parameters) - : []; + var result = await _queryManager.ExecuteQueryAsync(elasticQuery, queryParameters); - var result = await _queryManager.ExecuteQueryAsync(elasticQuery, queryParameters); - - return result; - } + return result; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPartFieldIndexSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPartFieldIndexSettingsDisplayDriver.cs index 72c3472b830..a2ac796aa7b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPartFieldIndexSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPartFieldIndexSettingsDisplayDriver.cs @@ -8,48 +8,47 @@ using OrchardCore.Search.Elasticsearch.Core.Models; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Drivers +namespace OrchardCore.Search.Elasticsearch.Drivers; + +public sealed class ContentPartFieldIndexSettingsDisplayDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class ContentPartFieldIndexSettingsDisplayDriver : ContentPartFieldDefinitionDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentPartFieldIndexSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public ContentPartFieldIndexSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - public override async Task EditAsync(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext context) + return Initialize("ElasticContentIndexSettings_Edit", model => { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) - { - return null; - } - - return Initialize("ElasticContentIndexSettings_Edit", model => - { - model.ElasticContentIndexSettings = contentPartFieldDefinition.GetSettings(); - }).Location("Content:10"); - } + model.ElasticContentIndexSettings = contentPartFieldDefinition.GetSettings(); + }).Location("Content:10"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition contentPartFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition contentPartFieldDefinition, UpdatePartFieldEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) - { - return null; - } + return null; + } - var model = new ElasticContentIndexSettingsViewModel(); + var model = new ElasticContentIndexSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model.ElasticContentIndexSettings); + context.Builder.WithSettings(model.ElasticContentIndexSettings); - return await EditAsync(contentPartFieldDefinition, context); - } + return await EditAsync(contentPartFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPickerFieldElasticEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPickerFieldElasticEditorSettingsDriver.cs index 70c7eb749d5..4033c9852e3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPickerFieldElasticEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentPickerFieldElasticEditorSettingsDriver.cs @@ -9,46 +9,45 @@ using OrchardCore.Search.Elasticsearch.Core.Models; using OrchardCore.Search.Elasticsearch.Core.Services; -namespace OrchardCore.Search.Elasticsearch.Drivers +namespace OrchardCore.Search.Elasticsearch.Drivers; + +public sealed class ContentPickerFieldElasticEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class ContentPickerFieldElasticEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; - public ContentPickerFieldElasticEditorSettingsDriver(ElasticIndexSettingsService elasticIndexSettingsService) - { - _elasticIndexSettingsService = elasticIndexSettingsService; - } + public ContentPickerFieldElasticEditorSettingsDriver(ElasticIndexSettingsService elasticIndexSettingsService) + { + _elasticIndexSettingsService = elasticIndexSettingsService; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("ContentPickerFieldElasticEditorSettings_Edit", async model => { - return Initialize("ContentPickerFieldElasticEditorSettings_Edit", async model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Index = settings.Index; + model.Index = settings.Index; - model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Editor"); - } + model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Editor"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Elasticsearch") { - if (partFieldDefinition.Editor() == "Elasticsearch") - { - var model = new ContentPickerFieldElasticEditorSettings(); + var model = new ContentPickerFieldElasticEditorSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); - } - - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(model); } - public override bool CanHandleModel(ContentPartFieldDefinition model) - { - return string.Equals("ContentPickerField", model.FieldDefinition.Name, StringComparison.Ordinal); - } + return Edit(partFieldDefinition, context); + } + + public override bool CanHandleModel(ContentPartFieldDefinition model) + { + return string.Equals("ContentPickerField", model.FieldDefinition.Name, StringComparison.Ordinal); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentTypePartIndexSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentTypePartIndexSettingsDisplayDriver.cs index 46c3391313a..09fe0469113 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentTypePartIndexSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ContentTypePartIndexSettingsDisplayDriver.cs @@ -8,48 +8,47 @@ using OrchardCore.Search.Elasticsearch.Core.Models; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Drivers +namespace OrchardCore.Search.Elasticsearch.Drivers; + +public sealed class ContentTypePartIndexSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class ContentTypePartIndexSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentTypePartIndexSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public ContentTypePartIndexSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - public override async Task EditAsync(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("ElasticContentIndexSettings_Edit", model => { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) - { - return null; - } - - return Initialize("ElasticContentIndexSettings_Edit", model => - { - model.ElasticContentIndexSettings = contentTypePartDefinition.GetSettings(); - }).Location("Content:10"); - } + model.ElasticContentIndexSettings = contentTypePartDefinition.GetSettings(); + }).Location("Content:10"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageElasticIndexes)) - { - return null; - } + return null; + } - var model = new ElasticContentIndexSettingsViewModel(); + var model = new ElasticContentIndexSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model.ElasticContentIndexSettings); + context.Builder.WithSettings(model.ElasticContentIndexSettings); - return await EditAsync(contentTypePartDefinition, context); - } + return await EditAsync(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexDeploymentStepDriver.cs index 80805a0f0eb..8400beec841 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexDeploymentStepDriver.cs @@ -6,52 +6,51 @@ using OrchardCore.Search.Elasticsearch.Core.Services; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Core.Deployment +namespace OrchardCore.Search.Elasticsearch.Core.Deployment; + +public sealed class ElasticIndexDeploymentStepDriver : DisplayDriver { - public sealed class ElasticIndexDeploymentStepDriver : DisplayDriver + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + + public ElasticIndexDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) { - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + _elasticIndexSettingsService = elasticIndexSettingsService; + } - public ElasticIndexDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) - { - _elasticIndexSettingsService = elasticIndexSettingsService; - } + public override Task DisplayAsync(ElasticIndexDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("ElasticIndexDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ElasticIndexDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(ElasticIndexDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(ElasticIndexDeploymentStep step, BuildEditorContext context) + { + return Initialize("ElasticIndexDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("ElasticIndexDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ElasticIndexDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.IndexNames; + model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(ElasticIndexDeploymentStep step, BuildEditorContext context) - { - return Initialize("ElasticIndexDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.IndexNames; - model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(ElasticIndexDeploymentStep step, UpdateEditorContext context) + { + step.IndexNames = []; + + await context.Updater.TryUpdateModelAsync(step, + Prefix, + x => x.IndexNames, + x => x.IncludeAll); - public override async Task UpdateAsync(ElasticIndexDeploymentStep step, UpdateEditorContext context) + // Don't have the selected option if include all. + if (step.IncludeAll) { step.IndexNames = []; - - await context.Updater.TryUpdateModelAsync(step, - Prefix, - x => x.IndexNames, - x => x.IncludeAll); - - // Don't have the selected option if include all. - if (step.IncludeAll) - { - step.IndexNames = []; - } - - return Edit(step, context); } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexRebuildDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexRebuildDeploymentStepDriver.cs index 2a436222f8d..8a3cd411dde 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexRebuildDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexRebuildDeploymentStepDriver.cs @@ -6,48 +6,47 @@ using OrchardCore.Search.Elasticsearch.Core.Services; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Core.Deployment +namespace OrchardCore.Search.Elasticsearch.Core.Deployment; + +public sealed class ElasticIndexRebuildDeploymentStepDriver : DisplayDriver { - public sealed class ElasticIndexRebuildDeploymentStepDriver : DisplayDriver + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + + public ElasticIndexRebuildDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) { - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + _elasticIndexSettingsService = elasticIndexSettingsService; + } - public ElasticIndexRebuildDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) - { - _elasticIndexSettingsService = elasticIndexSettingsService; - } + public override Task DisplayAsync(ElasticIndexRebuildDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("ElasticIndexRebuildDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ElasticIndexRebuildDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(ElasticIndexRebuildDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(ElasticIndexRebuildDeploymentStep step, BuildEditorContext context) + { + return Initialize("ElasticIndexRebuildDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("ElasticIndexRebuildDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ElasticIndexRebuildDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.Indices; + model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(ElasticIndexRebuildDeploymentStep step, BuildEditorContext context) - { - return Initialize("ElasticIndexRebuildDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.Indices; - model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(ElasticIndexRebuildDeploymentStep rebuildIndexStep, UpdateEditorContext context) + { + rebuildIndexStep.Indices = []; + + await context.Updater.TryUpdateModelAsync(rebuildIndexStep, Prefix, step => step.Indices, step => step.IncludeAll); - public override async Task UpdateAsync(ElasticIndexRebuildDeploymentStep rebuildIndexStep, UpdateEditorContext context) + if (rebuildIndexStep.IncludeAll) { rebuildIndexStep.Indices = []; - - await context.Updater.TryUpdateModelAsync(rebuildIndexStep, Prefix, step => step.Indices, step => step.IncludeAll); - - if (rebuildIndexStep.IncludeAll) - { - rebuildIndexStep.Indices = []; - } - - return Edit(rebuildIndexStep, context); } + + return Edit(rebuildIndexStep, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexResetDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexResetDeploymentStepDriver.cs index 724f4f2cb1b..f65835fd499 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexResetDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticIndexResetDeploymentStepDriver.cs @@ -6,48 +6,47 @@ using OrchardCore.Search.Elasticsearch.Core.Services; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Core.Deployment +namespace OrchardCore.Search.Elasticsearch.Core.Deployment; + +public sealed class ElasticIndexResetDeploymentStepDriver : DisplayDriver { - public sealed class ElasticIndexResetDeploymentStepDriver : DisplayDriver + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + + public ElasticIndexResetDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) { - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + _elasticIndexSettingsService = elasticIndexSettingsService; + } - public ElasticIndexResetDeploymentStepDriver(ElasticIndexSettingsService elasticIndexSettingsService) - { - _elasticIndexSettingsService = elasticIndexSettingsService; - } + public override Task DisplayAsync(ElasticIndexResetDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("ElasticIndexResetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ElasticIndexResetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(ElasticIndexResetDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(ElasticIndexResetDeploymentStep step, BuildEditorContext context) + { + return Initialize("ElasticIndexResetDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("ElasticIndexResetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ElasticIndexResetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.Indices; + model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(ElasticIndexResetDeploymentStep step, BuildEditorContext context) - { - return Initialize("ElasticIndexResetDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.Indices; - model.AllIndexNames = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(ElasticIndexResetDeploymentStep resetIndexStep, UpdateEditorContext context) + { + resetIndexStep.Indices = []; + + await context.Updater.TryUpdateModelAsync(resetIndexStep, Prefix, step => step.Indices, step => step.IncludeAll); - public override async Task UpdateAsync(ElasticIndexResetDeploymentStep resetIndexStep, UpdateEditorContext context) + if (resetIndexStep.IncludeAll) { resetIndexStep.Indices = []; - - await context.Updater.TryUpdateModelAsync(resetIndexStep, Prefix, step => step.Indices, step => step.IncludeAll); - - if (resetIndexStep.IncludeAll) - { - resetIndexStep.Indices = []; - } - - return Edit(resetIndexStep, context); } + + return Edit(resetIndexStep, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticQueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticQueryDisplayDriver.cs index 52fc490cebd..1b518f02163 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticQueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticQueryDisplayDriver.cs @@ -10,90 +10,89 @@ using OrchardCore.Search.Elasticsearch.Models; using OrchardCore.Search.Elasticsearch.ViewModels; -namespace OrchardCore.Search.Elasticsearch.Drivers +namespace OrchardCore.Search.Elasticsearch.Drivers; + +public sealed class ElasticQueryDisplayDriver : DisplayDriver { - public sealed class ElasticQueryDisplayDriver : DisplayDriver - { - private readonly ElasticIndexSettingsService _elasticIndexSettingsService; + private readonly ElasticIndexSettingsService _elasticIndexSettingsService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public ElasticQueryDisplayDriver( - IStringLocalizer stringLocalizer, - ElasticIndexSettingsService elasticIndexSettingsService) - { - _elasticIndexSettingsService = elasticIndexSettingsService; - S = stringLocalizer; - } + public ElasticQueryDisplayDriver( + IStringLocalizer stringLocalizer, + ElasticIndexSettingsService elasticIndexSettingsService) + { + _elasticIndexSettingsService = elasticIndexSettingsService; + S = stringLocalizer; + } - public override IDisplayResult Display(Query query, BuildDisplayContext context) + public override IDisplayResult Display(Query query, BuildDisplayContext context) + { + if (query.Source != ElasticQuerySource.SourceName) { - if (query.Source != ElasticQuerySource.SourceName) - { - return null; - } - - return Combine( - Dynamic("ElasticQuery_SummaryAdmin", model => { model.Query = query; }).Location("Content:5"), - Dynamic("ElasticQuery_Buttons_SummaryAdmin", model => { model.Query = query; }).Location("Actions:2") - ); + return null; } - public override IDisplayResult Edit(Query query, BuildEditorContext context) - { - if (query.Source != ElasticQuerySource.SourceName) - { - return null; - } + return Combine( + Dynamic("ElasticQuery_SummaryAdmin", model => { model.Query = query; }).Location("Content:5"), + Dynamic("ElasticQuery_Buttons_SummaryAdmin", model => { model.Query = query; }).Location("Actions:2") + ); + } - return Initialize("ElasticQuery_Edit", async model => - { - var metadata = query.As(); - - model.Query = metadata.Template; - model.Index = metadata.Index; - model.ReturnContentItems = query.ReturnContentItems; - model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - - // Extract query from the query string if we come from the main query editor. - if (string.IsNullOrEmpty(metadata.Template)) - { - await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); - } - }).Location("Content:5"); + public override IDisplayResult Edit(Query query, BuildEditorContext context) + { + if (query.Source != ElasticQuerySource.SourceName) + { + return null; } - public override async Task UpdateAsync(Query query, UpdateEditorContext context) + return Initialize("ElasticQuery_Edit", async model => { - if (query.Source != ElasticQuerySource.SourceName) - { - return null; - } + var metadata = query.As(); - var viewModel = new ElasticQueryViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, - m => m.Query, - m => m.Index, - m => m.ReturnContentItems); + model.Query = metadata.Template; + model.Index = metadata.Index; + model.ReturnContentItems = query.ReturnContentItems; + model.Indices = (await _elasticIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - if (string.IsNullOrWhiteSpace(viewModel.Query)) + // Extract query from the query string if we come from the main query editor. + if (string.IsNullOrEmpty(metadata.Template)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); + await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); } + }).Location("Content:5"); + } - if (string.IsNullOrWhiteSpace(viewModel.Index)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Index), S["The index field is required"]); - } + public override async Task UpdateAsync(Query query, UpdateEditorContext context) + { + if (query.Source != ElasticQuerySource.SourceName) + { + return null; + } - query.ReturnContentItems = viewModel.ReturnContentItems; - query.Put(new ElasticsearchQueryMetadata - { - Template = viewModel.Query, - Index = viewModel.Index, - }); + var viewModel = new ElasticQueryViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, + m => m.Query, + m => m.Index, + m => m.ReturnContentItems); + + if (string.IsNullOrWhiteSpace(viewModel.Query)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); + } - return Edit(query, context); + if (string.IsNullOrWhiteSpace(viewModel.Index)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Index), S["The index field is required"]); } + + query.ReturnContentItems = viewModel.ReturnContentItems; + query.Put(new ElasticsearchQueryMetadata + { + Template = viewModel.Query, + Index = viewModel.Index, + }); + + return Edit(query, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticSettingsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticSettingsDeploymentStepDriver.cs index 3c6a6780f9a..533969956cb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticSettingsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Drivers/ElasticSettingsDeploymentStepDriver.cs @@ -4,22 +4,21 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Search.Elasticsearch.Core.Deployment; -namespace OrchardCore.Search.Elasticsearch.Drivers +namespace OrchardCore.Search.Elasticsearch.Drivers; + +public sealed class ElasticSettingsDeploymentStepDriver : DisplayDriver { - public sealed class ElasticSettingsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(ElasticSettingsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(ElasticSettingsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("ElasticSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("ElasticSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("ElasticSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("ElasticSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(ElasticSettingsDeploymentStep step, BuildEditorContext context) - { - return View("ElasticSettingsDeploymentStep_Fields_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(ElasticSettingsDeploymentStep step, BuildEditorContext context) + { + return View("ElasticSettingsDeploymentStep_Fields_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs index 0cb9bf4af84..a5ed8a9673c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/ElasticQueryFieldTypeProvider.cs @@ -18,201 +18,200 @@ using OrchardCore.Search.Elasticsearch.Core.Services; using OrchardCore.Search.Elasticsearch.Models; -namespace OrchardCore.Search.Elasticsearch.GraphQL.Queries +namespace OrchardCore.Search.Elasticsearch.GraphQL.Queries; + +public class ElasticQueryFieldTypeProvider : ISchemaBuilder { - public class ElasticQueryFieldTypeProvider : ISchemaBuilder + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public ElasticQueryFieldTypeProvider(IHttpContextAccessor httpContextAccessor, ILogger logger) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } - public ElasticQueryFieldTypeProvider(IHttpContextAccessor httpContextAccessor, ILogger logger) - { - _httpContextAccessor = httpContextAccessor; - _logger = logger; - } + public Task GetIdentifierAsync() + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public Task GetIdentifierAsync() - { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + return queryManager.GetIdentifierAsync(); + } - return queryManager.GetIdentifierAsync(); - } + public async Task BuildAsync(ISchema schema) + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public async Task BuildAsync(ISchema schema) + var queries = await queryManager.ListQueriesBySourceAsync(ElasticQuerySource.SourceName); + + foreach (var query in queries) { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + if (string.IsNullOrWhiteSpace(query.Schema)) + continue; - var queries = await queryManager.ListQueriesBySourceAsync(ElasticQuerySource.SourceName); + var name = query.Name; - foreach (var query in queries) + try { - if (string.IsNullOrWhiteSpace(query.Schema)) + var querySchema = JObject.Parse(query.Schema); + if (!querySchema.ContainsKey("type")) + { + _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", name); continue; + } + var type = querySchema["type"].ToString(); + FieldType fieldType; - var name = query.Name; - - try + var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; + var metadata = query.As(); + if (query.ReturnContentItems && + type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) { - var querySchema = JObject.Parse(query.Schema); - if (!querySchema.ContainsKey("type")) - { - _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", name); - continue; - } - var type = querySchema["type"].ToString(); - FieldType fieldType; - - var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; - var metadata = query.As(); - if (query.ReturnContentItems && - type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) - { - var contentType = type.Remove(0, 12); - fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); - } - else - { - fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); - } - - if (fieldType != null) - { - schema.Query.AddField(fieldType); - } + var contentType = type.Remove(0, 12); + fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); } - catch (Exception e) + else { - _logger.LogError(e, "The Query '{Name}' has an invalid schema.", name); + fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); + } + + if (fieldType != null) + { + schema.Query.AddField(fieldType); } } + catch (Exception e) + { + _logger.LogError(e, "The Query '{Name}' has an invalid schema.", name); + } } + } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + { + var properties = querySchema["properties"]?.AsObject(); + if (properties == null) { - var properties = querySchema["properties"]?.AsObject(); - if (properties == null) - { - return null; - } + return null; + } - var typeType = new ObjectGraphType - { - Name = fieldTypeName - }; + var typeType = new ObjectGraphType + { + Name = fieldTypeName + }; - foreach (var child in properties) - { - var name = child.Key; - var nameLower = name.Replace('.', '_'); - var type = child.Value["type"].ToString(); - var description = child.Value["description"]?.ToString(); + foreach (var child in properties) + { + var name = child.Key; + var nameLower = name.Replace('.', '_'); + var type = child.Value["type"].ToString(); + var description = child.Value["description"]?.ToString(); - if (type == "string") + if (type == "string") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(StringGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(StringGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - field.Metadata.Add("Name", name); - typeType.AddField(field); - } - else if (type == "integer") + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; + field.Metadata.Add("Name", name); + typeType.AddField(field); + } + else if (type == "integer") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(IntGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(IntGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - - field.Metadata.Add("Name", name); - typeType.AddField(field); - } + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; + + field.Metadata.Add("Name", name); + typeType.AddField(field); } + } - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = new ListGraphType(typeType), - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeof(ListGraphType>) - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = new ListGraphType(typeType), + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeof(ListGraphType>) + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var iQuery = await queryManager.GetQueryAsync(query.Name); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var parameters = context.GetArgument("parameters"); + var parameters = context.GetArgument("parameters"); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return result.Items; - } - - return fieldType; + return result.Items; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) - { - var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); + return fieldType; + } - if (typeType == null) - { - return null; - } + private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + { + var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = typeType.ResolvedType, - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeType.Type - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + if (typeType == null) + { + return null; + } - var iQuery = await queryManager.GetQueryAsync(query.Name); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = typeType.ResolvedType, + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeType.Type + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var parameters = context.GetArgument("parameters"); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var parameters = context.GetArgument("parameters"); - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - return result.Items; - } + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return fieldType; + return result.Items; } + + return fieldType; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/Startup.cs index 1dfba7f2e77..e64c0c5acf7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/GraphQL/Startup.cs @@ -3,17 +3,16 @@ using OrchardCore.Modules; using OrchardCore.Search.Elasticsearch.GraphQL.Queries; -namespace OrchardCore.Search.Elasticsearch.GraphQL +namespace OrchardCore.Search.Elasticsearch.GraphQL; + +/// +/// These services are registered on the tenant service collection. +/// +[RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.Queries")] +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - [RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.Queries")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Startup.cs index f2fcae10a5f..093bbc6aa3d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/Startup.cs @@ -29,129 +29,128 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Search.Elasticsearch +namespace OrchardCore.Search.Elasticsearch; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) + { + _shellConfiguration = shellConfiguration; + } + + public override void ConfigureServices(IServiceCollection services) { - private readonly IShellConfiguration _shellConfiguration; + services.AddTransient, ElasticConnectionOptionsConfigurations>(); - public Startup(IShellConfiguration shellConfiguration) + services.AddSingleton((sp) => { - _shellConfiguration = shellConfiguration; - } + var options = sp.GetRequiredService>().Value; - public override void ConfigureServices(IServiceCollection services) + return new ElasticClient(options.GetConnectionSettings() ?? new ConnectionSettings()); + }); + + services.Configure(o => { - services.AddTransient, ElasticConnectionOptionsConfigurations>(); + var configuration = _shellConfiguration.GetSection(ElasticConnectionOptionsConfigurations.ConfigSectionName); - services.AddSingleton((sp) => - { - var options = sp.GetRequiredService>().Value; + o.IndexPrefix = configuration.GetValue(nameof(o.IndexPrefix)); - return new ElasticClient(options.GetConnectionSettings() ?? new ConnectionSettings()); - }); + var jsonNode = configuration.GetSection(nameof(o.Analyzers)).AsJsonNode(); + var jsonElement = JsonSerializer.Deserialize(jsonNode); - services.Configure(o => + var analyzersObject = JsonObject.Create(jsonElement, new JsonNodeOptions() { - var configuration = _shellConfiguration.GetSection(ElasticConnectionOptionsConfigurations.ConfigSectionName); + PropertyNameCaseInsensitive = true, + }); + if (analyzersObject != null) + { o.IndexPrefix = configuration.GetValue(nameof(o.IndexPrefix)); - var jsonNode = configuration.GetSection(nameof(o.Analyzers)).AsJsonNode(); - var jsonElement = JsonSerializer.Deserialize(jsonNode); - - var analyzersObject = JsonObject.Create(jsonElement, new JsonNodeOptions() + if (jsonNode is JsonObject jAnalyzers) { - PropertyNameCaseInsensitive = true, - }); - - if (analyzersObject != null) - { - o.IndexPrefix = configuration.GetValue(nameof(o.IndexPrefix)); - - if (jsonNode is JsonObject jAnalyzers) + foreach (var analyzer in jAnalyzers) { - foreach (var analyzer in jAnalyzers) + if (analyzer.Value is not JsonObject jAnalyzer) { - if (analyzer.Value is not JsonObject jAnalyzer) - { - continue; - } - - o.Analyzers.Add(analyzer.Key, jAnalyzer); + continue; } + + o.Analyzers.Add(analyzer.Key, jAnalyzer); } } + } - if (o.Analyzers.Count == 0) + if (o.Analyzers.Count == 0) + { + // When no analyzers are configured, we'll define a default analyzer. + o.Analyzers.Add(ElasticsearchConstants.DefaultAnalyzer, new JsonObject { - // When no analyzers are configured, we'll define a default analyzer. - o.Analyzers.Add(ElasticsearchConstants.DefaultAnalyzer, new JsonObject - { - ["type"] = "standard", - }); - } - }); - - services.AddElasticServices(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, ElasticQueryDisplayDriver>(); - services.AddDataMigration(); - services.AddScoped(); - } + ["type"] = "standard", + }); + } + }); + + services.AddElasticServices(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, ElasticQueryDisplayDriver>(); + services.AddDataMigration(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Search")] - public sealed class SearchStartup : StartupBase +[RequireFeatures("OrchardCore.Search")] +public sealed class SearchStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, ElasticSettingsDisplayDriver>(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped, ElasticSettingsDisplayDriver>(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddDeployment(); - services.AddDeployment(); - services.AddDeployment(); - } + services.AddDeployment(); + services.AddDeployment(); + services.AddDeployment(); + services.AddDeployment(); } +} - [Feature("OrchardCore.Search.Elasticsearch.Worker")] - public sealed class ElasticWorkerStartup : StartupBase +[Feature("OrchardCore.Search.Elasticsearch.Worker")] +public sealed class ElasticWorkerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddSingleton(); } +} - [Feature("OrchardCore.Search.Elasticsearch.ContentPicker")] - public sealed class ElasticContentPickerStartup : StartupBase +[Feature("OrchardCore.Search.Elasticsearch.ContentPicker")] +public sealed class ElasticContentPickerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddShapeAttributes(); - } + services.AddScoped(); + services.AddScoped(); + services.AddShapeAttributes(); } +} - [RequireFeatures("OrchardCore.ContentTypes")] - public sealed class ContentTypesStartup : StartupBase +[RequireFeatures("OrchardCore.ContentTypes")] +public sealed class ContentTypesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminIndexViewModel.cs index a41a87cb477..35f6e65cbe9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminIndexViewModel.cs @@ -2,37 +2,36 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class AdminIndexViewModel { - public class AdminIndexViewModel - { - public IEnumerable Indexes { get; set; } + public IEnumerable Indexes { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); + public ContentOptions Options { get; set; } = new ContentOptions(); - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } +} - public class ContentOptions - { - public ContentsBulkAction BulkAction { get; set; } +public class ContentOptions +{ + public ContentsBulkAction BulkAction { get; set; } - public string Search { get; set; } + public string Search { get; set; } - #region Lists to populate + #region Lists to populate - [BindNever] - public List ContentsBulkAction { get; set; } + [BindNever] + public List ContentsBulkAction { get; set; } - #endregion Lists to populate - } + #endregion Lists to populate +} - public enum ContentsBulkAction - { - None, - Reset, - Rebuild, - Remove - } +public enum ContentsBulkAction +{ + None, + Reset, + Rebuild, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminQueryViewModel.cs index f4406d00f6c..09386bd8ecb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/AdminQueryViewModel.cs @@ -2,27 +2,26 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class AdminQueryViewModel { - public class AdminQueryViewModel - { - public string DecodedQuery { get; set; } - public string IndexName { get; set; } - public string Parameters { get; set; } + public string DecodedQuery { get; set; } + public string IndexName { get; set; } + public string Parameters { get; set; } - [BindNever] - public long Count { get; set; } + [BindNever] + public long Count { get; set; } - [BindNever] - public string[] Indices { get; set; } + [BindNever] + public string[] Indices { get; set; } - [BindNever] - public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; + [BindNever] + public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; - [BindNever] - public IEnumerable> Documents { get; set; } = []; + [BindNever] + public IEnumerable> Documents { get; set; } = []; - [BindNever] - public IEnumerable> Fields { get; set; } = []; - } + [BindNever] + public IEnumerable> Fields { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticApiQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticApiQueryViewModel.cs index fd7870aa8ae..58abf70b3d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticApiQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticApiQueryViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticApiQueryViewModel { - public class ElasticApiQueryViewModel - { - public string IndexName { set; get; } - public string Query { set; get; } - public string Parameters { set; get; } - } + public string IndexName { set; get; } + public string Query { set; get; } + public string Parameters { set; get; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticContentIndexSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticContentIndexSettingsViewModel.cs index 7cae6a8180e..b4cb3d26d6b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticContentIndexSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticContentIndexSettingsViewModel.cs @@ -1,9 +1,8 @@ using OrchardCore.Search.Elasticsearch.Core.Models; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticContentIndexSettingsViewModel { - public class ElasticContentIndexSettingsViewModel - { - public ElasticContentIndexSettings ElasticContentIndexSettings { get; set; } - } + public ElasticContentIndexSettings ElasticContentIndexSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexDeploymentStepViewModel.cs index 9cde8bb3479..0c671e183e8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticIndexDeploymentStepViewModel { - public class ElasticIndexDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexRebuildDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexRebuildDeploymentStepViewModel.cs index a0876412b4d..45a98a542fe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexRebuildDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexRebuildDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticIndexRebuildDeploymentStepViewModel { - public class ElasticIndexRebuildDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexResetDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexResetDeploymentStepViewModel.cs index 056cbae9d82..cabc3337c64 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexResetDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexResetDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticIndexResetDeploymentStepViewModel { - public class ElasticIndexResetDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexSettingsViewModel.cs index 017ef230c5e..86644769990 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticIndexSettingsViewModel.cs @@ -2,32 +2,31 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticIndexSettingsViewModel { - public class ElasticIndexSettingsViewModel - { - public string IndexName { get; set; } + public string IndexName { get; set; } - public string AnalyzerName { get; set; } + public string AnalyzerName { get; set; } - public bool IndexLatest { get; set; } + public bool IndexLatest { get; set; } - public string Culture { get; set; } + public string Culture { get; set; } - public string[] IndexedContentTypes { get; set; } + public string[] IndexedContentTypes { get; set; } - public bool IsCreate { get; set; } + public bool IsCreate { get; set; } - public bool StoreSourceData { get; set; } + public bool StoreSourceData { get; set; } - #region List to populate + #region List to populate - [BindNever] - public IEnumerable Analyzers { get; set; } + [BindNever] + public IEnumerable Analyzers { get; set; } - [BindNever] - public IEnumerable Cultures { get; set; } + [BindNever] + public IEnumerable Cultures { get; set; } - #endregion List to populate - } + #endregion List to populate } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticQueryViewModel.cs index dc5b7ef7e88..47bcaf8627f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticQueryViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticQueryViewModel { - public class ElasticQueryViewModel - { - public string[] Indices { get; set; } - public string Index { get; set; } - public string Query { get; set; } - public bool ReturnContentItems { get; set; } - } + public string[] Indices { get; set; } + public string Index { get; set; } + public string Query { get; set; } + public bool ReturnContentItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticSettingsViewModel.cs index c06bc3f4ff1..38ec862292e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/ElasticSettingsViewModel.cs @@ -2,23 +2,22 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class ElasticSettingsViewModel { - public class ElasticSettingsViewModel - { - public string Analyzer { get; set; } + public string Analyzer { get; set; } - public string SearchIndex { get; set; } + public string SearchIndex { get; set; } - public IEnumerable SearchIndexes { get; set; } + public IEnumerable SearchIndexes { get; set; } - public string SearchFields { get; set; } + public string SearchFields { get; set; } - public string DefaultQuery { get; set; } + public string DefaultQuery { get; set; } - public string SearchType { get; set; } + public string SearchType { get; set; } - [BindNever] - public IEnumerable SearchTypes { get; set; } - } + [BindNever] + public IEnumerable SearchTypes { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/IndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/IndexViewModel.cs index c639a91e979..a7248b89a2f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/IndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/IndexViewModel.cs @@ -1,11 +1,10 @@ using System; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class IndexViewModel { - public class IndexViewModel - { - public string Name { get; set; } - public string AnalyzerName { get; set; } - public DateTime LastUpdateUtc { get; set; } - } + public string Name { get; set; } + public string AnalyzerName { get; set; } + public DateTime LastUpdateUtc { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/MappingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/MappingsViewModel.cs index 721771ae1e8..0d350b911f0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/MappingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/MappingsViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class MappingsViewModel { - public class MappingsViewModel - { - public string IndexName { get; set; } - public string Mappings { get; set; } - } + public string IndexName { get; set; } + public string Mappings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/QueryIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/QueryIndexViewModel.cs index 4ad74637422..663e9b9f783 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/QueryIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Elasticsearch/ViewModels/QueryIndexViewModel.cs @@ -2,17 +2,16 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Search.Elasticsearch.ViewModels +namespace OrchardCore.Search.Elasticsearch.ViewModels; + +public class QueryIndexViewModel { - public class QueryIndexViewModel - { - public string Query { get; set; } - public string IndexName { get; set; } + public string Query { get; set; } + public string IndexName { get; set; } - [BindNever] - public TimeSpan Duration { get; set; } + [BindNever] + public TimeSpan Duration { get; set; } - [BindNever] - public IEnumerable> Documents { get; set; } = []; - } + [BindNever] + public IEnumerable> Documents { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/AdminController.cs index 03fdec5f624..9237cfc2051 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/AdminController.cs @@ -31,485 +31,484 @@ using OrchardCore.Search.Lucene.ViewModels; using YesSql; -namespace OrchardCore.Search.Lucene.Controllers +namespace OrchardCore.Search.Lucene.Controllers; + +[Admin("Lucene/{action}/{id?}", "Lucene.{action}")] +public class AdminController : Controller { - [Admin("Lucene/{action}/{id?}", "Lucene.{action}")] - public class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly ISession _session; + private readonly LuceneIndexManager _luceneIndexManager; + private readonly LuceneIndexingService _luceneIndexingService; + private readonly IAuthorizationService _authorizationService; + private readonly INotifier _notifier; + private readonly LuceneAnalyzerManager _luceneAnalyzerManager; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly ILuceneQueryService _queryService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly PagerOptions _pagerOptions; + private readonly JavaScriptEncoder _javaScriptEncoder; + private readonly IShapeFactory _shapeFactory; + private readonly ILogger _logger; + private readonly IOptions _templateOptions; + private readonly ILocalizationService _localizationService; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + ISession session, + IContentDefinitionManager contentDefinitionManager, + LuceneIndexManager luceneIndexManager, + LuceneIndexingService luceneIndexingService, + IAuthorizationService authorizationService, + LuceneAnalyzerManager luceneAnalyzerManager, + LuceneIndexSettingsService luceneIndexSettingsService, + ILuceneQueryService queryService, + ILiquidTemplateManager liquidTemplateManager, + INotifier notifier, + IOptions pagerOptions, + JavaScriptEncoder javaScriptEncoder, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + ILogger logger, + IOptions templateOptions, + ILocalizationService localizationService) + { + _session = session; + _luceneIndexManager = luceneIndexManager; + _luceneIndexingService = luceneIndexingService; + _authorizationService = authorizationService; + _luceneAnalyzerManager = luceneAnalyzerManager; + _luceneIndexSettingsService = luceneIndexSettingsService; + _queryService = queryService; + _liquidTemplateManager = liquidTemplateManager; + _contentDefinitionManager = contentDefinitionManager; + _notifier = notifier; + _pagerOptions = pagerOptions.Value; + _javaScriptEncoder = javaScriptEncoder; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + _logger = logger; + _templateOptions = templateOptions; + _localizationService = localizationService; + } + + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly ISession _session; - private readonly LuceneIndexManager _luceneIndexManager; - private readonly LuceneIndexingService _luceneIndexingService; - private readonly IAuthorizationService _authorizationService; - private readonly INotifier _notifier; - private readonly LuceneAnalyzerManager _luceneAnalyzerManager; - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - private readonly ILuceneQueryService _queryService; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly PagerOptions _pagerOptions; - private readonly JavaScriptEncoder _javaScriptEncoder; - private readonly IShapeFactory _shapeFactory; - private readonly ILogger _logger; - private readonly IOptions _templateOptions; - private readonly ILocalizationService _localizationService; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - ISession session, - IContentDefinitionManager contentDefinitionManager, - LuceneIndexManager luceneIndexManager, - LuceneIndexingService luceneIndexingService, - IAuthorizationService authorizationService, - LuceneAnalyzerManager luceneAnalyzerManager, - LuceneIndexSettingsService luceneIndexSettingsService, - ILuceneQueryService queryService, - ILiquidTemplateManager liquidTemplateManager, - INotifier notifier, - IOptions pagerOptions, - JavaScriptEncoder javaScriptEncoder, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - ILogger logger, - IOptions templateOptions, - ILocalizationService localizationService) - { - _session = session; - _luceneIndexManager = luceneIndexManager; - _luceneIndexingService = luceneIndexingService; - _authorizationService = authorizationService; - _luceneAnalyzerManager = luceneAnalyzerManager; - _luceneIndexSettingsService = luceneIndexSettingsService; - _queryService = queryService; - _liquidTemplateManager = liquidTemplateManager; - _contentDefinitionManager = contentDefinitionManager; - _notifier = notifier; - _pagerOptions = pagerOptions.Value; - _javaScriptEncoder = javaScriptEncoder; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - _logger = logger; - _templateOptions = templateOptions; - _localizationService = localizationService; + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); } - public async Task Index(ContentOptions options, PagerParameters pagerParameters) + var indexes = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(i => new IndexViewModel { Name = i.IndexName }); + + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var count = indexes.Count(); + var results = indexes; + + if (!string.IsNullOrWhiteSpace(options.Search)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } + results = results.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); + } - var indexes = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(i => new IndexViewModel { Name = i.IndexName }); + results = results + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ToList(); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var count = indexes.Count(); - var results = indexes; + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - results = results.Where(q => q.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); - } + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - results = results - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); + var model = new AdminIndexViewModel + { + Indexes = results, + Options = options, + Pager = await _shapeFactory.PagerAsync(pager, count, routeData) + }; + + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Reset"], nameof(ContentsBulkAction.Reset)), + new SelectListItem(S["Rebuild"], nameof(ContentsBulkAction.Rebuild)), + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; + + return View(model); + } - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(AdminIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - if (!string.IsNullOrEmpty(options.Search)) + public async Task Edit(string indexName = null) + { + var IsCreate = string.IsNullOrWhiteSpace(indexName); + var settings = new LuceneIndexSettings(); + + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); + } + + if (!IsCreate) + { + settings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); + + if (settings == null) { - routeData.Values.TryAdd(_optionsSearch, options.Search); + return NotFound(); } + } - var model = new AdminIndexViewModel - { - Indexes = results, - Options = options, - Pager = await _shapeFactory.PagerAsync(pager, count, routeData) - }; - - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Reset"], nameof(ContentsBulkAction.Reset)), - new SelectListItem(S["Rebuild"], nameof(ContentsBulkAction.Rebuild)), - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + var model = new LuceneIndexSettingsViewModel + { + IsCreate = IsCreate, + IndexName = IsCreate ? string.Empty : settings.IndexName, + AnalyzerName = IsCreate ? "standardanalyzer" : settings.AnalyzerName, + IndexLatest = settings.IndexLatest, + Culture = settings.Culture, + Cultures = ILocalizationService.GetAllCulturesAndAliases() + .Select(x => new SelectListItem { Text = x.Name + " (" + x.DisplayName + ")", Value = x.Name }).Prepend(new SelectListItem { Text = S["Any culture"], Value = "any" }), + Analyzers = _luceneAnalyzerManager.GetAnalyzers() + .Select(x => new SelectListItem { Text = x.Name, Value = x.Name }), + IndexedContentTypes = IsCreate ? (await _contentDefinitionManager.ListTypeDefinitionsAsync()) + .Select(x => x.Name).ToArray() : settings.IndexedContentTypes, + StoreSourceData = !IsCreate && settings.StoreSourceData + }; + + return View(model); + } - return View(model); + [HttpPost, ActionName(nameof(Edit))] + public async Task EditPost(LuceneIndexSettingsViewModel model, string[] indexedContentTypes) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(AdminIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + ValidateModel(model); - public async Task Edit(string indexName = null) + if (model.IsCreate) { - var IsCreate = string.IsNullOrWhiteSpace(indexName); - var settings = new LuceneIndexSettings(); - - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + if (_luceneIndexManager.Exists(model.IndexName)) { - return Forbid(); + ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["An index named {0} already exists.", model.IndexName]); } - - if (!IsCreate) + } + else + { + if (!_luceneIndexManager.Exists(model.IndexName)) { - settings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); - - if (settings == null) - { - return NotFound(); - } + ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["An index named {0} doesn't exist.", model.IndexName]); } + } - var model = new LuceneIndexSettingsViewModel - { - IsCreate = IsCreate, - IndexName = IsCreate ? string.Empty : settings.IndexName, - AnalyzerName = IsCreate ? "standardanalyzer" : settings.AnalyzerName, - IndexLatest = settings.IndexLatest, - Culture = settings.Culture, - Cultures = ILocalizationService.GetAllCulturesAndAliases() - .Select(x => new SelectListItem { Text = x.Name + " (" + x.DisplayName + ")", Value = x.Name }).Prepend(new SelectListItem { Text = S["Any culture"], Value = "any" }), - Analyzers = _luceneAnalyzerManager.GetAnalyzers() - .Select(x => new SelectListItem { Text = x.Name, Value = x.Name }), - IndexedContentTypes = IsCreate ? (await _contentDefinitionManager.ListTypeDefinitionsAsync()) - .Select(x => x.Name).ToArray() : settings.IndexedContentTypes, - StoreSourceData = !IsCreate && settings.StoreSourceData - }; + if (!ModelState.IsValid) + { + var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); + model.Cultures = supportedCultures + .Select(c => new SelectListItem + { + Text = $"{c} ({CultureInfo.GetCultureInfo(c).DisplayName})", + Value = c + }).Prepend(new SelectListItem { Text = S["Any culture"], Value = "any" }); + model.Analyzers = _luceneAnalyzerManager.GetAnalyzers() + .Select(x => new SelectListItem { Text = x.Name, Value = x.Name }); return View(model); } - [HttpPost, ActionName(nameof(Edit))] - public async Task EditPost(LuceneIndexSettingsViewModel model, string[] indexedContentTypes) + if (model.IsCreate) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + try { - return Forbid(); - } + var settings = new LuceneIndexSettings { IndexName = model.IndexName, AnalyzerName = model.AnalyzerName, IndexLatest = model.IndexLatest, IndexedContentTypes = indexedContentTypes, Culture = model.Culture ?? "", StoreSourceData = model.StoreSourceData }; - ValidateModel(model); - - if (model.IsCreate) - { - if (_luceneIndexManager.Exists(model.IndexName)) - { - ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["An index named {0} already exists.", model.IndexName]); - } + // We call Rebuild in order to reset the index state cursor too in case the same index + // name was also used previously. + await _luceneIndexingService.CreateIndexAsync(settings); } - else + catch (Exception e) { - if (!_luceneIndexManager.Exists(model.IndexName)) - { - ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["An index named {0} doesn't exist.", model.IndexName]); - } + await _notifier.ErrorAsync(H["An error occurred while creating the index."]); + _logger.LogError(e, "An error occurred while creating an index."); + return View(model); } - if (!ModelState.IsValid) + await _notifier.SuccessAsync(H["Index {0} created successfully.", model.IndexName]); + } + else + { + try { - var supportedCultures = await _localizationService.GetSupportedCulturesAsync(); + var settings = new LuceneIndexSettings { IndexName = model.IndexName, AnalyzerName = model.AnalyzerName, IndexLatest = model.IndexLatest, IndexedContentTypes = indexedContentTypes, Culture = model.Culture ?? "", StoreSourceData = model.StoreSourceData }; - model.Cultures = supportedCultures - .Select(c => new SelectListItem - { - Text = $"{c} ({CultureInfo.GetCultureInfo(c).DisplayName})", - Value = c - }).Prepend(new SelectListItem { Text = S["Any culture"], Value = "any" }); - model.Analyzers = _luceneAnalyzerManager.GetAnalyzers() - .Select(x => new SelectListItem { Text = x.Name, Value = x.Name }); + await _luceneIndexingService.UpdateIndexAsync(settings); + } + catch (Exception e) + { + await _notifier.ErrorAsync(H["An error occurred while editing the index."]); + _logger.LogError(e, "An error occurred while editing an index."); return View(model); } - if (model.IsCreate) - { - try - { - var settings = new LuceneIndexSettings { IndexName = model.IndexName, AnalyzerName = model.AnalyzerName, IndexLatest = model.IndexLatest, IndexedContentTypes = indexedContentTypes, Culture = model.Culture ?? "", StoreSourceData = model.StoreSourceData }; + await _notifier.SuccessAsync(H["Index {0} modified successfully, please consider doing a rebuild on the index.", model.IndexName]); + } - // We call Rebuild in order to reset the index state cursor too in case the same index - // name was also used previously. - await _luceneIndexingService.CreateIndexAsync(settings); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while creating the index."]); - _logger.LogError(e, "An error occurred while creating an index."); - return View(model); - } + return RedirectToAction(nameof(Index)); + } + + [HttpPost] + public async Task Reset(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); + } - await _notifier.SuccessAsync(H["Index {0} created successfully.", model.IndexName]); + var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(id); + + if (luceneIndexSettings != null) + { + if (!_luceneIndexManager.Exists(id)) + { + await _luceneIndexingService.CreateIndexAsync(luceneIndexSettings); + await _luceneIndexingService.ProcessContentItemsAsync(id); } else { - try - { - var settings = new LuceneIndexSettings { IndexName = model.IndexName, AnalyzerName = model.AnalyzerName, IndexLatest = model.IndexLatest, IndexedContentTypes = indexedContentTypes, Culture = model.Culture ?? "", StoreSourceData = model.StoreSourceData }; - - await _luceneIndexingService.UpdateIndexAsync(settings); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while editing the index."]); - _logger.LogError(e, "An error occurred while editing an index."); - return View(model); - } - - await _notifier.SuccessAsync(H["Index {0} modified successfully, please consider doing a rebuild on the index.", model.IndexName]); + _luceneIndexingService.ResetIndexAsync(id); + await _luceneIndexingService.ProcessContentItemsAsync(id); } - return RedirectToAction(nameof(Index)); + await _notifier.SuccessAsync(H["Index {0} reset successfully.", id]); } - [HttpPost] - public async Task Reset(string id) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } + return RedirectToAction(nameof(Index)); + } - var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(id); + [HttpPost] + public async Task Rebuild(string id) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); + } - if (luceneIndexSettings != null) - { - if (!_luceneIndexManager.Exists(id)) - { - await _luceneIndexingService.CreateIndexAsync(luceneIndexSettings); - await _luceneIndexingService.ProcessContentItemsAsync(id); - } - else - { - _luceneIndexingService.ResetIndexAsync(id); - await _luceneIndexingService.ProcessContentItemsAsync(id); - } + var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(id); - await _notifier.SuccessAsync(H["Index {0} reset successfully.", id]); - } + if (luceneIndexSettings != null) + { + await _luceneIndexingService.RebuildIndexAsync(id); + await _luceneIndexingService.ProcessContentItemsAsync(id); - return RedirectToAction(nameof(Index)); + await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", id]); } - - [HttpPost] - public async Task Rebuild(string id) + else { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } + return NotFound(); + } - var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(id); + return RedirectToAction(nameof(Index)); + } - if (luceneIndexSettings != null) - { - await _luceneIndexingService.RebuildIndexAsync(id); - await _luceneIndexingService.ProcessContentItemsAsync(id); + [HttpPost] + public async Task Delete(LuceneIndexSettingsViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); + } - await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", id]); - } - else - { - return NotFound(); - } + var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(model.IndexName); - return RedirectToAction(nameof(Index)); + if (luceneIndexSettings == null) + { + return NotFound(); } - [HttpPost] - public async Task Delete(LuceneIndexSettingsViewModel model) + try { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } + await _luceneIndexingService.DeleteIndexAsync(model.IndexName); - var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(model.IndexName); + await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); + } + catch (Exception e) + { + await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); + _logger.LogError(e, "An error occurred while deleting the index '{IndexName}'.", model.IndexName); + } - if (luceneIndexSettings == null) - { - return NotFound(); - } + return RedirectToAction(nameof(Index)); + } - try - { - await _luceneIndexingService.DeleteIndexAsync(model.IndexName); + public Task Query(string indexName, string query) + { + query = string.IsNullOrWhiteSpace(query) ? "" : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)); + return Query(new AdminQueryViewModel { IndexName = indexName, DecodedQuery = query }); + } - await _notifier.SuccessAsync(H["Index {0} deleted successfully.", model.IndexName]); - } - catch (Exception e) - { - await _notifier.ErrorAsync(H["An error occurred while deleting the index."]); - _logger.LogError(e, "An error occurred while deleting the index '{IndexName}'.", model.IndexName); - } + [HttpPost] + public async Task Query(AdminQueryViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) + { + return Forbid(); + } + + model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + // Can't query if there are no indices + if (model.Indices.Length == 0) + { return RedirectToAction(nameof(Index)); } - public Task Query(string indexName, string query) + if (string.IsNullOrEmpty(model.IndexName)) { - query = string.IsNullOrWhiteSpace(query) ? "" : System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(query)); - return Query(new AdminQueryViewModel { IndexName = indexName, DecodedQuery = query }); + model.IndexName = model.Indices[0]; } - [HttpPost] - public async Task Query(AdminQueryViewModel model) + if (!_luceneIndexManager.Exists(model.IndexName)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } - - model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + return NotFound(); + } - // Can't query if there are no indices - if (model.Indices.Length == 0) - { - return RedirectToAction(nameof(Index)); - } + if (string.IsNullOrWhiteSpace(model.DecodedQuery)) + { + return View(model); + } - if (string.IsNullOrEmpty(model.IndexName)) - { - model.IndexName = model.Indices[0]; - } + if (string.IsNullOrEmpty(model.Parameters)) + { + model.Parameters = "{ }"; + } - if (!_luceneIndexManager.Exists(model.IndexName)) - { - return NotFound(); - } + var stopwatch = new Stopwatch(); + stopwatch.Start(); - if (string.IsNullOrWhiteSpace(model.DecodedQuery)) - { - return View(model); - } + await _luceneIndexManager.SearchAsync(model.IndexName, async searcher => + { + var analyzer = _luceneAnalyzerManager.CreateAnalyzer(await _luceneIndexSettingsService.GetIndexAnalyzerAsync(model.IndexName)); + var context = new LuceneQueryContext(searcher, LuceneSettings.DefaultVersion, analyzer); - if (string.IsNullOrEmpty(model.Parameters)) - { - model.Parameters = "{ }"; - } + var parameters = JConvert.DeserializeObject>(model.Parameters); - var stopwatch = new Stopwatch(); - stopwatch.Start(); + var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions.Value)))); - await _luceneIndexManager.SearchAsync(model.IndexName, async searcher => + try { - var analyzer = _luceneAnalyzerManager.CreateAnalyzer(await _luceneIndexSettingsService.GetIndexAnalyzerAsync(model.IndexName)); - var context = new LuceneQueryContext(searcher, LuceneSettings.DefaultVersion, analyzer); - - var parameters = JConvert.DeserializeObject>(model.Parameters); - - var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(model.DecodedQuery, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions.Value)))); + var parameterizedQuery = JsonNode.Parse(tokenizedContent).AsObject(); + var luceneTopDocs = await _queryService.SearchAsync(context, parameterizedQuery); - try + if (luceneTopDocs != null) { - var parameterizedQuery = JsonNode.Parse(tokenizedContent).AsObject(); - var luceneTopDocs = await _queryService.SearchAsync(context, parameterizedQuery); - - if (luceneTopDocs != null) - { - model.Documents = luceneTopDocs.TopDocs.ScoreDocs.Select(hit => searcher.Doc(hit.Doc)).ToList(); - model.Count = luceneTopDocs.Count; - } - } - catch (Exception e) - { - _logger.LogError(e, "Error while executing query"); - ModelState.AddModelError(nameof(model.DecodedQuery), S["Invalid query : {0}", e.Message]); + model.Documents = luceneTopDocs.TopDocs.ScoreDocs.Select(hit => searcher.Doc(hit.Doc)).ToList(); + model.Count = luceneTopDocs.Count; } + } + catch (Exception e) + { + _logger.LogError(e, "Error while executing query"); + ModelState.AddModelError(nameof(model.DecodedQuery), S["Invalid query : {0}", e.Message]); + } - stopwatch.Stop(); - model.Elapsed = stopwatch.Elapsed; - }); + stopwatch.Stop(); + model.Elapsed = stopwatch.Elapsed; + }); - return View(model); - } + return View(model); + } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageLuceneIndexes)) - { - return Forbid(); - } + return Forbid(); + } - if (itemIds?.Count() > 0) - { - var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(); - var checkedContentItems = luceneIndexSettings.Where(x => itemIds.Contains(x.IndexName)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) + if (itemIds?.Count() > 0) + { + var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(); + var checkedContentItems = luceneIndexSettings.Where(x => itemIds.Contains(x.IndexName)); + switch (options.BulkAction) + { + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _luceneIndexingService.DeleteIndexAsync(item.IndexName); + } + await _notifier.SuccessAsync(H["Indices successfully removed."]); + break; + case ContentsBulkAction.Reset: + foreach (var item in checkedContentItems) + { + if (!_luceneIndexManager.Exists(item.IndexName)) { - await _luceneIndexingService.DeleteIndexAsync(item.IndexName); + return NotFound(); } - await _notifier.SuccessAsync(H["Indices successfully removed."]); - break; - case ContentsBulkAction.Reset: - foreach (var item in checkedContentItems) - { - if (!_luceneIndexManager.Exists(item.IndexName)) - { - return NotFound(); - } - _luceneIndexingService.ResetIndexAsync(item.IndexName); - await _luceneIndexingService.ProcessContentItemsAsync(item.IndexName); + _luceneIndexingService.ResetIndexAsync(item.IndexName); + await _luceneIndexingService.ProcessContentItemsAsync(item.IndexName); - await _notifier.SuccessAsync(H["Index {0} reset successfully.", item.IndexName]); - } - break; - case ContentsBulkAction.Rebuild: - foreach (var item in checkedContentItems) + await _notifier.SuccessAsync(H["Index {0} reset successfully.", item.IndexName]); + } + break; + case ContentsBulkAction.Rebuild: + foreach (var item in checkedContentItems) + { + if (!_luceneIndexManager.Exists(item.IndexName)) { - if (!_luceneIndexManager.Exists(item.IndexName)) - { - return NotFound(); - } + return NotFound(); + } - await _luceneIndexingService.RebuildIndexAsync(item.IndexName); - await _luceneIndexingService.ProcessContentItemsAsync(item.IndexName); + await _luceneIndexingService.RebuildIndexAsync(item.IndexName); + await _luceneIndexingService.ProcessContentItemsAsync(item.IndexName); - await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", item.IndexName]); - } - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } + await _notifier.SuccessAsync(H["Index {0} rebuilt successfully.", item.IndexName]); + } + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } - - return RedirectToAction(nameof(Index)); } - private void ValidateModel(LuceneIndexSettingsViewModel model) + return RedirectToAction(nameof(Index)); + } + + private void ValidateModel(LuceneIndexSettingsViewModel model) + { + if (model.IndexedContentTypes == null || model.IndexedContentTypes.Length < 1) { - if (model.IndexedContentTypes == null || model.IndexedContentTypes.Length < 1) - { - ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexedContentTypes), S["At least one content type selection is required."]); - } + ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexedContentTypes), S["At least one content type selection is required."]); + } - if (string.IsNullOrWhiteSpace(model.IndexName)) - { - ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["The index name is required."]); - } - else if (model.IndexName.ToSafeName() != model.IndexName) - { - ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["The index name contains forbidden characters."]); - } + if (string.IsNullOrWhiteSpace(model.IndexName)) + { + ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["The index name is required."]); + } + else if (model.IndexName.ToSafeName() != model.IndexName) + { + ModelState.AddModelError(nameof(LuceneIndexSettingsViewModel.IndexName), S["The index name contains forbidden characters."]); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/LuceneApiController.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/LuceneApiController.cs index dcca3f52166..183aa31e73e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/LuceneApiController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Controllers/LuceneApiController.cs @@ -7,98 +7,97 @@ using OrchardCore.Queries; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene.Controllers +namespace OrchardCore.Search.Lucene.Controllers; + +[Route("api/lucene")] +[ApiController] +[Authorize(AuthenticationSchemes = "Api")] +[IgnoreAntiforgeryToken] +[AllowAnonymous] +public sealed class LuceneApiController : ControllerBase { - [Route("api/lucene")] - [ApiController] - [Authorize(AuthenticationSchemes = "Api")] - [IgnoreAntiforgeryToken] - [AllowAnonymous] - public sealed class LuceneApiController : ControllerBase + private readonly IAuthorizationService _authorizationService; + private readonly IQueryManager _queryManager; + + public LuceneApiController( + IAuthorizationService authorizationService, + IQueryManager queryManager) { - private readonly IAuthorizationService _authorizationService; - private readonly IQueryManager _queryManager; + _authorizationService = authorizationService; + _queryManager = queryManager; + } - public LuceneApiController( - IAuthorizationService authorizationService, - IQueryManager queryManager) + [HttpGet] + [Route("content")] + public async Task Content([FromQuery] LuceneQueryModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) { - _authorizationService = authorizationService; - _queryManager = queryManager; + return this.ChallengeOrForbid("Api"); } - [HttpGet] - [Route("content")] - public async Task Content([FromQuery] LuceneQueryModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) - { - return this.ChallengeOrForbid("Api"); - } + var result = await LuceneQueryApiAsync(queryModel, returnContentItems: true); - var result = await LuceneQueryApiAsync(queryModel, returnContentItems: true); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpPost] + [Route("content")] + public async Task ContentPost(LuceneQueryModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) + { + return this.ChallengeOrForbid(); } - [HttpPost] - [Route("content")] - public async Task ContentPost(LuceneQueryModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) - { - return this.ChallengeOrForbid(); - } + var result = await LuceneQueryApiAsync(queryModel, returnContentItems: true); - var result = await LuceneQueryApiAsync(queryModel, returnContentItems: true); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpGet] + [Route("documents")] + public async Task Documents([FromQuery] LuceneQueryModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) + { + return this.ChallengeOrForbid(); } - [HttpGet] - [Route("documents")] - public async Task Documents([FromQuery] LuceneQueryModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) - { - return this.ChallengeOrForbid(); - } + var result = await LuceneQueryApiAsync(queryModel); - var result = await LuceneQueryApiAsync(queryModel); + return new ObjectResult(result); + } - return new ObjectResult(result); + [HttpPost] + [Route("documents")] + public async Task DocumentsPost(LuceneQueryModel queryModel) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) + { + return this.ChallengeOrForbid("Api"); } - [HttpPost] - [Route("documents")] - public async Task DocumentsPost(LuceneQueryModel queryModel) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.QueryLuceneApi)) - { - return this.ChallengeOrForbid("Api"); - } + var result = await LuceneQueryApiAsync(queryModel); - var result = await LuceneQueryApiAsync(queryModel); + return new ObjectResult(result); + } - return new ObjectResult(result); - } + private async Task LuceneQueryApiAsync(LuceneQueryModel queryModel, bool returnContentItems = false) + { + var luceneQuery = await _queryManager.NewAsync(LuceneQuerySource.SourceName); + luceneQuery.ReturnContentItems = returnContentItems; - private async Task LuceneQueryApiAsync(LuceneQueryModel queryModel, bool returnContentItems = false) + luceneQuery.Put(new LuceneQueryMetadata() { - var luceneQuery = await _queryManager.NewAsync(LuceneQuerySource.SourceName); - luceneQuery.ReturnContentItems = returnContentItems; + Index = queryModel.IndexName, + Template = queryModel.Query, + }); - luceneQuery.Put(new LuceneQueryMetadata() - { - Index = queryModel.IndexName, - Template = queryModel.Query, - }); + var queryParameters = queryModel.Parameters != null + ? JConvert.DeserializeObject>(queryModel.Parameters) + : []; - var queryParameters = queryModel.Parameters != null - ? JConvert.DeserializeObject>(queryModel.Parameters) - : []; - - return await _queryManager.ExecuteQueryAsync(luceneQuery, queryParameters); - } + return await _queryManager.ExecuteQueryAsync(luceneQuery, queryParameters); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentSource.cs index a2c024f00c8..00802cfee38 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentSource.cs @@ -5,48 +5,47 @@ using OrchardCore.Deployment; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public class LuceneIndexDeploymentSource : IDeploymentSource { - public class LuceneIndexDeploymentSource : IDeploymentSource + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + + public LuceneIndexDeploymentSource(LuceneIndexSettingsService luceneIndexSettingsService) { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + _luceneIndexSettingsService = luceneIndexSettingsService; + } - public LuceneIndexDeploymentSource(LuceneIndexSettingsService luceneIndexSettingsService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not LuceneIndexDeploymentStep luceneIndexStep) { - _luceneIndexSettingsService = luceneIndexSettingsService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not LuceneIndexDeploymentStep luceneIndexStep) - { - return; - } + var indexSettings = await _luceneIndexSettingsService.GetSettingsAsync(); - var indexSettings = await _luceneIndexSettingsService.GetSettingsAsync(); + var data = new JsonArray(); + var indicesToAdd = luceneIndexStep.IncludeAll ? indexSettings.Select(x => x.IndexName).ToArray() : luceneIndexStep.IndexNames; - var data = new JsonArray(); - var indicesToAdd = luceneIndexStep.IncludeAll ? indexSettings.Select(x => x.IndexName).ToArray() : luceneIndexStep.IndexNames; - - foreach (var index in indexSettings) + foreach (var index in indexSettings) + { + if (indicesToAdd.Contains(index.IndexName)) { - if (indicesToAdd.Contains(index.IndexName)) + var indexSettingsDict = new Dictionary { - var indexSettingsDict = new Dictionary - { - { index.IndexName, index }, - }; + { index.IndexName, index }, + }; - data.Add(JObject.FromObject(indexSettingsDict)); - } + data.Add(JObject.FromObject(indexSettingsDict)); } - - // Adding Lucene settings - result.Steps.Add(new JsonObject - { - ["name"] = "lucene-index", - ["Indices"] = data, - }); } + + // Adding Lucene settings + result.Steps.Add(new JsonObject + { + ["name"] = "lucene-index", + ["Indices"] = data, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStep.cs index 3ee686c9946..dae65615236 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStep.cs @@ -1,19 +1,18 @@ using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +/// +/// Adds layers to a . +/// +public class LuceneIndexDeploymentStep : DeploymentStep { - /// - /// Adds layers to a . - /// - public class LuceneIndexDeploymentStep : DeploymentStep + public LuceneIndexDeploymentStep() { - public LuceneIndexDeploymentStep() - { - Name = "LuceneIndex"; - } + Name = "LuceneIndex"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] IndexNames { get; set; } - } + public string[] IndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStepDriver.cs index cf956e05eb4..7974aef2c0f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexDeploymentStepDriver.cs @@ -5,52 +5,51 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public sealed class LuceneIndexDeploymentStepDriver : DisplayDriver { - public sealed class LuceneIndexDeploymentStepDriver : DisplayDriver + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + + public LuceneIndexDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + _luceneIndexSettingsService = luceneIndexSettingsService; + } - public LuceneIndexDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) - { - _luceneIndexSettingsService = luceneIndexSettingsService; - } + public override Task DisplayAsync(LuceneIndexDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("LuceneIndexDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("LuceneIndexDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(LuceneIndexDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(LuceneIndexDeploymentStep step, BuildEditorContext context) + { + return Initialize("LuceneIndexDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("LuceneIndexDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("LuceneIndexDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.IndexNames; + model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(LuceneIndexDeploymentStep step, BuildEditorContext context) - { - return Initialize("LuceneIndexDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.IndexNames; - model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(LuceneIndexDeploymentStep step, UpdateEditorContext context) + { + step.IndexNames = []; + + await context.Updater.TryUpdateModelAsync(step, + Prefix, + x => x.IndexNames, + x => x.IncludeAll); - public override async Task UpdateAsync(LuceneIndexDeploymentStep step, UpdateEditorContext context) + // don't have the selected option if include all + if (step.IncludeAll) { step.IndexNames = []; - - await context.Updater.TryUpdateModelAsync(step, - Prefix, - x => x.IndexNames, - x => x.IncludeAll); - - // don't have the selected option if include all - if (step.IncludeAll) - { - step.IndexNames = []; - } - - return Edit(step, context); } + + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentSource.cs index c2952037285..bac3bf0ecc5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentSource.cs @@ -2,29 +2,28 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public class LuceneIndexRebuildDeploymentSource : IDeploymentSource { - public class LuceneIndexRebuildDeploymentSource : IDeploymentSource + public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - var luceneIndexRebuildStep = step as LuceneIndexRebuildDeploymentStep; + var luceneIndexRebuildStep = step as LuceneIndexRebuildDeploymentStep; - if (luceneIndexRebuildStep == null) - { - return Task.CompletedTask; - } + if (luceneIndexRebuildStep == null) + { + return Task.CompletedTask; + } - var indicesToRebuild = luceneIndexRebuildStep.IncludeAll ? [] : luceneIndexRebuildStep.IndexNames; + var indicesToRebuild = luceneIndexRebuildStep.IncludeAll ? [] : luceneIndexRebuildStep.IndexNames; - result.Steps.Add(new JsonObject - { - ["name"] = "lucene-index-rebuild", - ["includeAll"] = luceneIndexRebuildStep.IncludeAll, - ["Indices"] = JArray.FromObject(indicesToRebuild), - }); + result.Steps.Add(new JsonObject + { + ["name"] = "lucene-index-rebuild", + ["includeAll"] = luceneIndexRebuildStep.IncludeAll, + ["Indices"] = JArray.FromObject(indicesToRebuild), + }); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStep.cs index 1cb69648465..2366fb0fb9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStep.cs @@ -1,19 +1,18 @@ using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +/// +/// Adds rebuild Lucene index task to a . +/// +public class LuceneIndexRebuildDeploymentStep : DeploymentStep { - /// - /// Adds rebuild Lucene index task to a . - /// - public class LuceneIndexRebuildDeploymentStep : DeploymentStep + public LuceneIndexRebuildDeploymentStep() { - public LuceneIndexRebuildDeploymentStep() - { - Name = "LuceneIndexRebuild"; - } + Name = "LuceneIndexRebuild"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] IndexNames { get; set; } - } + public string[] IndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStepDriver.cs index c8ffeae3271..db1567f67d0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexRebuildDeploymentStepDriver.cs @@ -5,48 +5,47 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public sealed class LuceneIndexRebuildDeploymentStepDriver : DisplayDriver { - public sealed class LuceneIndexRebuildDeploymentStepDriver : DisplayDriver + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + + public LuceneIndexRebuildDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + _luceneIndexSettingsService = luceneIndexSettingsService; + } - public LuceneIndexRebuildDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) - { - _luceneIndexSettingsService = luceneIndexSettingsService; - } + public override Task DisplayAsync(LuceneIndexRebuildDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("LuceneIndexRebuildDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("LuceneIndexRebuildDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(LuceneIndexRebuildDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(LuceneIndexRebuildDeploymentStep step, BuildEditorContext context) + { + return Initialize("LuceneIndexRebuildDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("LuceneIndexRebuildDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("LuceneIndexRebuildDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.IndexNames; + model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(LuceneIndexRebuildDeploymentStep step, BuildEditorContext context) - { - return Initialize("LuceneIndexRebuildDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.IndexNames; - model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(LuceneIndexRebuildDeploymentStep rebuildIndexStep, UpdateEditorContext context) + { + rebuildIndexStep.IndexNames = []; + + await context.Updater.TryUpdateModelAsync(rebuildIndexStep, Prefix, step => step.IndexNames, step => step.IncludeAll); - public override async Task UpdateAsync(LuceneIndexRebuildDeploymentStep rebuildIndexStep, UpdateEditorContext context) + if (rebuildIndexStep.IncludeAll) { rebuildIndexStep.IndexNames = []; - - await context.Updater.TryUpdateModelAsync(rebuildIndexStep, Prefix, step => step.IndexNames, step => step.IncludeAll); - - if (rebuildIndexStep.IncludeAll) - { - rebuildIndexStep.IndexNames = []; - } - - return Edit(rebuildIndexStep, context); } + + return Edit(rebuildIndexStep, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentSource.cs index 91500be0020..8784c7c23c0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentSource.cs @@ -2,29 +2,28 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public class LuceneIndexResetDeploymentSource : IDeploymentSource { - public class LuceneIndexResetDeploymentSource : IDeploymentSource + public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) { - public Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - var luceneIndexResetStep = step as LuceneIndexResetDeploymentStep; + var luceneIndexResetStep = step as LuceneIndexResetDeploymentStep; - if (luceneIndexResetStep == null) - { - return Task.CompletedTask; - } + if (luceneIndexResetStep == null) + { + return Task.CompletedTask; + } - var indicesToReset = luceneIndexResetStep.IncludeAll ? [] : luceneIndexResetStep.IndexNames; + var indicesToReset = luceneIndexResetStep.IncludeAll ? [] : luceneIndexResetStep.IndexNames; - result.Steps.Add(new JsonObject - { - ["name"] = "lucene-index-reset", - ["includeAll"] = luceneIndexResetStep.IncludeAll, - ["Indices"] = JArray.FromObject(indicesToReset), - }); + result.Steps.Add(new JsonObject + { + ["name"] = "lucene-index-reset", + ["includeAll"] = luceneIndexResetStep.IncludeAll, + ["Indices"] = JArray.FromObject(indicesToReset), + }); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStep.cs index 80a878f2163..9080f34336f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStep.cs @@ -1,19 +1,18 @@ using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +/// +/// Adds reset Lucene index task to a . +/// +public class LuceneIndexResetDeploymentStep : DeploymentStep { - /// - /// Adds reset Lucene index task to a . - /// - public class LuceneIndexResetDeploymentStep : DeploymentStep + public LuceneIndexResetDeploymentStep() { - public LuceneIndexResetDeploymentStep() - { - Name = "LuceneIndexReset"; - } + Name = "LuceneIndexReset"; + } - public bool IncludeAll { get; set; } = true; + public bool IncludeAll { get; set; } = true; - public string[] IndexNames { get; set; } - } + public string[] IndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStepDriver.cs index de9ecb1b52d..f6c3bb88d15 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneIndexResetDeploymentStepDriver.cs @@ -5,48 +5,47 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public sealed class LuceneIndexResetDeploymentStepDriver : DisplayDriver { - public sealed class LuceneIndexResetDeploymentStepDriver : DisplayDriver + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + + public LuceneIndexResetDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + _luceneIndexSettingsService = luceneIndexSettingsService; + } - public LuceneIndexResetDeploymentStepDriver(LuceneIndexSettingsService luceneIndexSettingsService) - { - _luceneIndexSettingsService = luceneIndexSettingsService; - } + public override Task DisplayAsync(LuceneIndexResetDeploymentStep step, BuildDisplayContext context) + { + return + CombineAsync( + View("LuceneIndexResetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("LuceneIndexResetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override Task DisplayAsync(LuceneIndexResetDeploymentStep step, BuildDisplayContext context) + public override IDisplayResult Edit(LuceneIndexResetDeploymentStep step, BuildEditorContext context) + { + return Initialize("LuceneIndexResetDeploymentStep_Fields_Edit", async model => { - return - CombineAsync( - View("LuceneIndexResetDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("LuceneIndexResetDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + model.IncludeAll = step.IncludeAll; + model.IndexNames = step.IndexNames; + model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Content"); + } - public override IDisplayResult Edit(LuceneIndexResetDeploymentStep step, BuildEditorContext context) - { - return Initialize("LuceneIndexResetDeploymentStep_Fields_Edit", async model => - { - model.IncludeAll = step.IncludeAll; - model.IndexNames = step.IndexNames; - model.AllIndexNames = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Content"); - } + public override async Task UpdateAsync(LuceneIndexResetDeploymentStep resetIndexStep, UpdateEditorContext context) + { + resetIndexStep.IndexNames = []; + + await context.Updater.TryUpdateModelAsync(resetIndexStep, Prefix, step => step.IndexNames, step => step.IncludeAll); - public override async Task UpdateAsync(LuceneIndexResetDeploymentStep resetIndexStep, UpdateEditorContext context) + if (resetIndexStep.IncludeAll) { resetIndexStep.IndexNames = []; - - await context.Updater.TryUpdateModelAsync(resetIndexStep, Prefix, step => step.IndexNames, step => step.IncludeAll); - - if (resetIndexStep.IncludeAll) - { - resetIndexStep.IndexNames = []; - } - - return Edit(resetIndexStep, context); } + + return Edit(resetIndexStep, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentSource.cs index 40191ae032d..c02938120c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentSource.cs @@ -2,32 +2,31 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public class LuceneSettingsDeploymentSource : IDeploymentSource { - public class LuceneSettingsDeploymentSource : IDeploymentSource + private readonly LuceneIndexingService _luceneIndexingService; + + public LuceneSettingsDeploymentSource(LuceneIndexingService luceneIndexingService) { - private readonly LuceneIndexingService _luceneIndexingService; + _luceneIndexingService = luceneIndexingService; + } - public LuceneSettingsDeploymentSource(LuceneIndexingService luceneIndexingService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not LuceneSettingsDeploymentStep) { - _luceneIndexingService = luceneIndexingService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not LuceneSettingsDeploymentStep) - { - return; - } - - var luceneSettings = await _luceneIndexingService.GetLuceneSettingsAsync(); + var luceneSettings = await _luceneIndexingService.GetLuceneSettingsAsync(); - // Adding Lucene settings - result.Steps.Add(new JsonObject - { - ["name"] = "Settings", - ["LuceneSettings"] = JObject.FromObject(luceneSettings), - }); - } + // Adding Lucene settings + result.Steps.Add(new JsonObject + { + ["name"] = "Settings", + ["LuceneSettings"] = JObject.FromObject(luceneSettings), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStep.cs index 711f79457e6..6d532469c0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +/// +/// Adds layers to a . +/// +public class LuceneSettingsDeploymentStep : DeploymentStep { - /// - /// Adds layers to a . - /// - public class LuceneSettingsDeploymentStep : DeploymentStep + public LuceneSettingsDeploymentStep() { - public LuceneSettingsDeploymentStep() - { - Name = "LuceneSettings"; - } + Name = "LuceneSettings"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStepDriver.cs index e70565b01d0..d743928a324 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Deployment/LuceneSettingsDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Search.Lucene.Deployment +namespace OrchardCore.Search.Lucene.Deployment; + +public sealed class LuceneSettingsDeploymentStepDriver : DisplayDriver { - public sealed class LuceneSettingsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(LuceneSettingsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(LuceneSettingsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("LuceneSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("LuceneSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("LuceneSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("LuceneSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(LuceneSettingsDeploymentStep step, BuildEditorContext context) - { - return View("LuceneSettingsDeploymentStep_Fields_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(LuceneSettingsDeploymentStep step, BuildEditorContext context) + { + return View("LuceneSettingsDeploymentStep_Fields_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneQueryDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneQueryDisplayDriver.cs index fe4bb3de489..1371a786d17 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneQueryDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneQueryDisplayDriver.cs @@ -9,90 +9,89 @@ using OrchardCore.Search.Lucene.Model; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Drivers +namespace OrchardCore.Search.Lucene.Drivers; + +public sealed class LuceneQueryDisplayDriver : DisplayDriver { - public sealed class LuceneQueryDisplayDriver : DisplayDriver - { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public LuceneQueryDisplayDriver( - IStringLocalizer stringLocalizer, - LuceneIndexSettingsService luceneIndexSettingsService) - { - _luceneIndexSettingsService = luceneIndexSettingsService; - S = stringLocalizer; - } + public LuceneQueryDisplayDriver( + IStringLocalizer stringLocalizer, + LuceneIndexSettingsService luceneIndexSettingsService) + { + _luceneIndexSettingsService = luceneIndexSettingsService; + S = stringLocalizer; + } - public override IDisplayResult Display(Query query, BuildDisplayContext context) + public override IDisplayResult Display(Query query, BuildDisplayContext context) + { + if (query.Source != LuceneQuerySource.SourceName) { - if (query.Source != LuceneQuerySource.SourceName) - { - return null; - } - - return Combine( - Dynamic("LuceneQuery_SummaryAdmin", model => { model.Query = query; }).Location("Content:5"), - Dynamic("LuceneQuery_Buttons_SummaryAdmin", model => { model.Query = query; }).Location("Actions:2") - ); + return null; } - public override IDisplayResult Edit(Query query, BuildEditorContext context) - { - if (query.Source != LuceneQuerySource.SourceName) - { - return null; - } + return Combine( + Dynamic("LuceneQuery_SummaryAdmin", model => { model.Query = query; }).Location("Content:5"), + Dynamic("LuceneQuery_Buttons_SummaryAdmin", model => { model.Query = query; }).Location("Actions:2") + ); + } - return Initialize("LuceneQuery_Edit", async model => - { - var metadata = query.As(); - - model.Query = metadata.Template; - model.Index = metadata.Index; - model.ReturnContentItems = query.ReturnContentItems; - model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - - // Extract query from the query string if we come from the main query editor. - if (string.IsNullOrEmpty(metadata.Template)) - { - await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); - } - }).Location("Content:5"); + public override IDisplayResult Edit(Query query, BuildEditorContext context) + { + if (query.Source != LuceneQuerySource.SourceName) + { + return null; } - public override async Task UpdateAsync(Query query, UpdateEditorContext context) + return Initialize("LuceneQuery_Edit", async model => { - if (query.Source != LuceneQuerySource.SourceName) - { - return null; - } + var metadata = query.As(); - var viewModel = new LuceneQueryViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, Prefix, - m => m.Query, - m => m.Index, - m => m.ReturnContentItems); + model.Query = metadata.Template; + model.Index = metadata.Index; + model.ReturnContentItems = query.ReturnContentItems; + model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - if (string.IsNullOrWhiteSpace(viewModel.Query)) + // Extract query from the query string if we come from the main query editor. + if (string.IsNullOrEmpty(metadata.Template)) { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); + await context.Updater.TryUpdateModelAsync(model, string.Empty, m => m.Query); } + }).Location("Content:5"); + } - if (string.IsNullOrWhiteSpace(viewModel.Index)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Index), S["The index field is required"]); - } + public override async Task UpdateAsync(Query query, UpdateEditorContext context) + { + if (query.Source != LuceneQuerySource.SourceName) + { + return null; + } - query.ReturnContentItems = viewModel.ReturnContentItems; - query.Put(new LuceneQueryMetadata() - { - Template = viewModel.Query, - Index = viewModel.Index, - }); + var viewModel = new LuceneQueryViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, Prefix, + m => m.Query, + m => m.Index, + m => m.ReturnContentItems); + + if (string.IsNullOrWhiteSpace(viewModel.Query)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Query), S["The query field is required"]); + } - return Edit(query, context); + if (string.IsNullOrWhiteSpace(viewModel.Index)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Index), S["The index field is required"]); } + + query.ReturnContentItems = viewModel.ReturnContentItems; + query.Put(new LuceneQueryMetadata() + { + Template = viewModel.Query, + Index = viewModel.Index, + }); + + return Edit(query, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneSettingsDisplayDriver.cs index 466cdf9afd8..e62d2b1f6c3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Drivers/LuceneSettingsDisplayDriver.cs @@ -10,66 +10,65 @@ using OrchardCore.Search.Lucene.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Search.Lucene.Drivers +namespace OrchardCore.Search.Lucene.Drivers; + +public sealed class LuceneSettingsDisplayDriver : SiteDisplayDriver { - public sealed class LuceneSettingsDisplayDriver : SiteDisplayDriver + private static readonly char[] _separator = [',', ' ']; + + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public LuceneSettingsDisplayDriver( + LuceneIndexSettingsService luceneIndexSettingsService, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService) { - private static readonly char[] _separator = [',', ' ']; + _luceneIndexSettingsService = luceneIndexSettingsService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + protected override string SettingsGroupId + => SearchConstants.SearchSettingsGroupId; - public LuceneSettingsDisplayDriver( - LuceneIndexSettingsService luceneIndexSettingsService, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService) + public override async Task EditAsync(ISite site, LuceneSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLuceneIndexes)) { - _luceneIndexSettingsService = luceneIndexSettingsService; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - protected override string SettingsGroupId - => SearchConstants.SearchSettingsGroupId; - - public override async Task EditAsync(ISite site, LuceneSettings settings, BuildEditorContext context) + return Initialize("LuceneSettings_Edit", async model => { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLuceneIndexes)) - { - return null; - } + model.SearchIndex = settings.SearchIndex; + model.SearchFields = string.Join(", ", settings.DefaultSearchFields ?? []); + model.SearchIndexes = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName); + model.AllowLuceneQueriesInSearch = settings.AllowLuceneQueriesInSearch; + }).Location("Content:2#Lucene;15") + .OnGroup(SettingsGroupId); + } - return Initialize("LuceneSettings_Edit", async model => - { - model.SearchIndex = settings.SearchIndex; - model.SearchFields = string.Join(", ", settings.DefaultSearchFields ?? []); - model.SearchIndexes = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName); - model.AllowLuceneQueriesInSearch = settings.AllowLuceneQueriesInSearch; - }).Location("Content:2#Lucene;15") - .OnGroup(SettingsGroupId); - } + public override async Task UpdateAsync(ISite site, LuceneSettings section, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task UpdateAsync(ISite site, LuceneSettings section, UpdateEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLuceneIndexes)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageLuceneIndexes)) - { - return null; - } + return null; + } - var model = new LuceneSettingsViewModel(); + var model = new LuceneSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - section.SearchIndex = model.SearchIndex; - section.DefaultSearchFields = model.SearchFields?.Split(_separator, StringSplitOptions.RemoveEmptyEntries); - section.AllowLuceneQueriesInSearch = model.AllowLuceneQueriesInSearch; + section.SearchIndex = model.SearchIndex; + section.DefaultSearchFields = model.SearchFields?.Split(_separator, StringSplitOptions.RemoveEmptyEntries); + section.AllowLuceneQueriesInSearch = model.AllowLuceneQueriesInSearch; - return await EditAsync(site, section, context); - } + return await EditAsync(site, section, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs index 1a3192a3689..a9ed0cd84e0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/LuceneQueryFieldTypeProvider.cs @@ -17,206 +17,205 @@ using OrchardCore.Search.Lucene; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Queries.Lucene.GraphQL.Queries +namespace OrchardCore.Queries.Lucene.GraphQL.Queries; + +public class LuceneQueryFieldTypeProvider : ISchemaBuilder { - public class LuceneQueryFieldTypeProvider : ISchemaBuilder + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ILogger _logger; + + public LuceneQueryFieldTypeProvider( + IHttpContextAccessor httpContextAccessor, + ILogger logger) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ILogger _logger; + _httpContextAccessor = httpContextAccessor; + _logger = logger; + } - public LuceneQueryFieldTypeProvider( - IHttpContextAccessor httpContextAccessor, - ILogger logger) - { - _httpContextAccessor = httpContextAccessor; - _logger = logger; - } + public Task GetIdentifierAsync() + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public Task GetIdentifierAsync() - { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + return queryManager.GetIdentifierAsync(); + } - return queryManager.GetIdentifierAsync(); - } + public async Task BuildAsync(ISchema schema) + { + var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); - public async Task BuildAsync(ISchema schema) - { - var queryManager = _httpContextAccessor.HttpContext.RequestServices.GetService(); + var queries = await queryManager.ListQueriesBySourceAsync(LuceneQuerySource.SourceName); - var queries = await queryManager.ListQueriesBySourceAsync(LuceneQuerySource.SourceName); + foreach (var query in queries) + { + if (string.IsNullOrWhiteSpace(query.Schema)) + { + continue; + } - foreach (var query in queries) + try { - if (string.IsNullOrWhiteSpace(query.Schema)) + var querySchema = JObject.Parse(query.Schema); + if (!querySchema.ContainsKey("type")) { + _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", query.Name); + continue; } - try - { - var querySchema = JObject.Parse(query.Schema); - if (!querySchema.ContainsKey("type")) - { - _logger.LogError("The Query '{Name}' schema is invalid, the 'type' property was not found.", query.Name); - - continue; - } - - var type = querySchema["type"].ToString(); - FieldType fieldType; - - var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; - var metadata = query.As(); + var type = querySchema["type"].ToString(); + FieldType fieldType; - if (query.ReturnContentItems && - type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) - { - var contentType = type.Remove(0, 12); - fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); - } - else - { - fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); - } + var fieldTypeName = querySchema["fieldTypeName"]?.ToString() ?? query.Name; + var metadata = query.As(); - if (fieldType != null) - { - schema.Query.AddField(fieldType); - } + if (query.ReturnContentItems && + type.StartsWith("ContentItem/", StringComparison.OrdinalIgnoreCase)) + { + var contentType = type.Remove(0, 12); + fieldType = BuildContentTypeFieldType(schema, contentType, query, fieldTypeName); } - catch (Exception e) + else { - _logger.LogError(e, "The Query '{Name}' has an invalid schema.", query.Name); + fieldType = BuildSchemaBasedFieldType(query, querySchema, fieldTypeName); } + + if (fieldType != null) + { + schema.Query.AddField(fieldType); + } + } + catch (Exception e) + { + _logger.LogError(e, "The Query '{Name}' has an invalid schema.", query.Name); } } + } - private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + private static FieldType BuildSchemaBasedFieldType(Query query, JsonNode querySchema, string fieldTypeName) + { + var properties = querySchema["properties"]?.AsObject(); + if (properties == null) { - var properties = querySchema["properties"]?.AsObject(); - if (properties == null) - { - return null; - } + return null; + } - var typeType = new ObjectGraphType - { - Name = fieldTypeName - }; + var typeType = new ObjectGraphType + { + Name = fieldTypeName + }; - foreach (var child in properties) - { - var name = child.Key; - var nameLower = name.Replace('.', '_'); - var type = child.Value["type"].ToString(); - var description = child.Value["description"]?.ToString(); + foreach (var child in properties) + { + var name = child.Key; + var nameLower = name.Replace('.', '_'); + var type = child.Value["type"].ToString(); + var description = child.Value["description"]?.ToString(); - if (type == "string") + if (type == "string") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(StringGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(StringGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - field.Metadata.Add("Name", name); - typeType.AddField(field); - } - else if (type == "integer") + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; + field.Metadata.Add("Name", name); + typeType.AddField(field); + } + else if (type == "integer") + { + var field = new FieldType() { - var field = new FieldType() + Name = nameLower, + Description = description, + Type = typeof(IntGraphType), + Resolver = new FuncFieldResolver(context => { - Name = nameLower, - Description = description, - Type = typeof(IntGraphType), - Resolver = new FuncFieldResolver(context => - { - var source = context.Source; - return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); - }), - }; - - field.Metadata.Add("Name", name); - typeType.AddField(field); - } - } + var source = context.Source; + return source[context.FieldDefinition.Metadata["Name"].ToString()].ToObject(); + }), + }; - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = new ListGraphType(typeType), - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeof(ListGraphType>) - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + field.Metadata.Add("Name", name); + typeType.AddField(field); + } + } - var iQuery = await queryManager.GetQueryAsync(query.Name); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = new ListGraphType(typeType), + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeof(ListGraphType>) + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var parameters = context.GetArgument("parameters"); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var parameters = context.GetArgument("parameters"); - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - return result.Items; - } + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return fieldType; + return result.Items; } - private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) - { - var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); + return fieldType; + } - if (typeType == null) - { - return null; - } + private static FieldType BuildContentTypeFieldType(ISchema schema, string contentType, Query query, string fieldTypeName) + { + var typeType = schema.Query.Fields.OfType().FirstOrDefault(x => x.Name == contentType); - var fieldType = new FieldType - { - Arguments = new QueryArguments( - new QueryArgument { Name = "parameters" } - ), - Name = fieldTypeName, - Description = "Represents the " + query.Source + " Query : " + query.Name, - ResolvedType = typeType.ResolvedType, - Resolver = new LockedAsyncFieldResolver(ResolveAsync), - Type = typeType.Type - }; - - async ValueTask ResolveAsync(IResolveFieldContext context) - { - var queryManager = context.RequestServices.GetRequiredService(); + if (typeType == null) + { + return null; + } - var iQuery = await queryManager.GetQueryAsync(query.Name); + var fieldType = new FieldType + { + Arguments = new QueryArguments( + new QueryArgument { Name = "parameters" } + ), + Name = fieldTypeName, + Description = "Represents the " + query.Source + " Query : " + query.Name, + ResolvedType = typeType.ResolvedType, + Resolver = new LockedAsyncFieldResolver(ResolveAsync), + Type = typeType.Type + }; + + async ValueTask ResolveAsync(IResolveFieldContext context) + { + var queryManager = context.RequestServices.GetRequiredService(); - var parameters = context.GetArgument("parameters"); + var iQuery = await queryManager.GetQueryAsync(query.Name); - var queryParameters = parameters != null - ? JConvert.DeserializeObject>(parameters) - : []; + var parameters = context.GetArgument("parameters"); - var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); + var queryParameters = parameters != null + ? JConvert.DeserializeObject>(parameters) + : []; - return result.Items; - } + var result = await queryManager.ExecuteQueryAsync(iQuery, queryParameters); - return fieldType; + return result.Items; } + + return fieldType; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/Startup.cs index 9c7e2f8af0b..88b900009cb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/GraphQL/Startup.cs @@ -3,17 +3,16 @@ using OrchardCore.Modules; using OrchardCore.Queries.Lucene.GraphQL.Queries; -namespace OrchardCore.Search.Lucene.GraphQL +namespace OrchardCore.Search.Lucene.GraphQL; + +/// +/// These services are registered on the tenant service collection. +/// +[RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.Queries")] +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - [RequireFeatures("OrchardCore.Apis.GraphQL", "OrchardCore.Queries")] - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddSingleton(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Handler/LuceneIndexingContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Handler/LuceneIndexingContentHandler.cs index e81a8e09112..ffc985a36fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Handler/LuceneIndexingContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Handler/LuceneIndexingContentHandler.cs @@ -14,106 +14,105 @@ using OrchardCore.Modules; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene.Handlers +namespace OrchardCore.Search.Lucene.Handlers; + +public class LuceneIndexingContentHandler : ContentHandlerBase { - public class LuceneIndexingContentHandler : ContentHandlerBase + private readonly List _contexts = []; + private readonly IHttpContextAccessor _httpContextAccessor; + + public LuceneIndexingContentHandler(IHttpContextAccessor httpContextAccessor) { - private readonly List _contexts = []; - private readonly IHttpContextAccessor _httpContextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public override Task PublishedAsync(PublishContentContext context) => AddContextAsync(context); + public override Task CreatedAsync(CreateContentContext context) => AddContextAsync(context); + public override Task UpdatedAsync(UpdateContentContext context) => AddContextAsync(context); + public override Task RemovedAsync(RemoveContentContext context) => AddContextAsync(context); + public override Task UnpublishedAsync(PublishContentContext context) => AddContextAsync(context); - public LuceneIndexingContentHandler(IHttpContextAccessor httpContextAccessor) + private Task AddContextAsync(ContentContextBase context) + { + // Do not index a preview content item. + if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) { - _httpContextAccessor = httpContextAccessor; + return Task.CompletedTask; } - public override Task PublishedAsync(PublishContentContext context) => AddContextAsync(context); - public override Task CreatedAsync(CreateContentContext context) => AddContextAsync(context); - public override Task UpdatedAsync(UpdateContentContext context) => AddContextAsync(context); - public override Task RemovedAsync(RemoveContentContext context) => AddContextAsync(context); - public override Task UnpublishedAsync(PublishContentContext context) => AddContextAsync(context); + if (context.ContentItem.Id == 0) + { + // Ignore that case, when Update is called on a content item which has not be "created" yet. + return Task.CompletedTask; + } - private Task AddContextAsync(ContentContextBase context) + if (_contexts.Count == 0) { - // Do not index a preview content item. - if (_httpContextAccessor.HttpContext?.Features.Get()?.Previewing == true) - { - return Task.CompletedTask; - } + var contexts = _contexts; - if (context.ContentItem.Id == 0) - { - // Ignore that case, when Update is called on a content item which has not be "created" yet. - return Task.CompletedTask; - } + // Using a local var prevents the lambda from holding a ref on this scoped service. + ShellScope.AddDeferredTask(scope => IndexingAsync(scope, contexts)); + } - if (_contexts.Count == 0) - { - var contexts = _contexts; + _contexts.Add(context); - // Using a local var prevents the lambda from holding a ref on this scoped service. - ShellScope.AddDeferredTask(scope => IndexingAsync(scope, contexts)); - } + return Task.CompletedTask; + } - _contexts.Add(context); + private static async Task IndexingAsync(ShellScope scope, IEnumerable contexts) + { + var services = scope.ServiceProvider; + var contentManager = services.GetRequiredService(); + var contentItemIndexHandlers = services.GetServices(); + var luceneIndexManager = services.GetRequiredService(); + var luceneIndexSettingsService = services.GetRequiredService(); + var logger = services.GetRequiredService>(); + + // Multiple items may have been updated in the same scope, e.g through a recipe. + var contextsGroupById = contexts.GroupBy(c => c.ContentItem.ContentItemId, c => c); + + // Get all contexts for each content item id. + foreach (var ContextsById in contextsGroupById) + { + // Only process the last context. + var context = ContextsById.Last(); - return Task.CompletedTask; - } + ContentItem published = null, latest = null; + bool publishedLoaded = false, latestLoaded = false; - private static async Task IndexingAsync(ShellScope scope, IEnumerable contexts) - { - var services = scope.ServiceProvider; - var contentManager = services.GetRequiredService(); - var contentItemIndexHandlers = services.GetServices(); - var luceneIndexManager = services.GetRequiredService(); - var luceneIndexSettingsService = services.GetRequiredService(); - var logger = services.GetRequiredService>(); - - // Multiple items may have been updated in the same scope, e.g through a recipe. - var contextsGroupById = contexts.GroupBy(c => c.ContentItem.ContentItemId, c => c); - - // Get all contexts for each content item id. - foreach (var ContextsById in contextsGroupById) + foreach (var indexSettings in await luceneIndexSettingsService.GetSettingsAsync()) { - // Only process the last context. - var context = ContextsById.Last(); + var cultureAspect = await contentManager.PopulateAspectAsync(context.ContentItem); + var culture = cultureAspect.HasCulture ? cultureAspect.Culture.Name : null; + var ignoreIndexedCulture = indexSettings.Culture != "any" && culture != indexSettings.Culture; - ContentItem published = null, latest = null; - bool publishedLoaded = false, latestLoaded = false; - - foreach (var indexSettings in await luceneIndexSettingsService.GetSettingsAsync()) + if (indexSettings.IndexedContentTypes.Contains(context.ContentItem.ContentType) && !ignoreIndexedCulture) { - var cultureAspect = await contentManager.PopulateAspectAsync(context.ContentItem); - var culture = cultureAspect.HasCulture ? cultureAspect.Culture.Name : null; - var ignoreIndexedCulture = indexSettings.Culture != "any" && culture != indexSettings.Culture; + if (!indexSettings.IndexLatest && !publishedLoaded) + { + publishedLoaded = true; + published = await contentManager.GetAsync(context.ContentItem.ContentItemId, VersionOptions.Published); + } + + if (indexSettings.IndexLatest && !latestLoaded) + { + latestLoaded = true; + latest = await contentManager.GetAsync(context.ContentItem.ContentItemId, VersionOptions.Latest); + } - if (indexSettings.IndexedContentTypes.Contains(context.ContentItem.ContentType) && !ignoreIndexedCulture) + var contentItem = !indexSettings.IndexLatest ? published : latest; + + if (contentItem == null) + { + await luceneIndexManager.DeleteDocumentsAsync(indexSettings.IndexName, new string[] { context.ContentItem.ContentItemId }); + } + else { - if (!indexSettings.IndexLatest && !publishedLoaded) - { - publishedLoaded = true; - published = await contentManager.GetAsync(context.ContentItem.ContentItemId, VersionOptions.Published); - } - - if (indexSettings.IndexLatest && !latestLoaded) - { - latestLoaded = true; - latest = await contentManager.GetAsync(context.ContentItem.ContentItemId, VersionOptions.Latest); - } - - var contentItem = !indexSettings.IndexLatest ? published : latest; - - if (contentItem == null) - { - await luceneIndexManager.DeleteDocumentsAsync(indexSettings.IndexName, new string[] { context.ContentItem.ContentItemId }); - } - else - { - var buildIndexContext = new BuildIndexContext(new DocumentIndex(contentItem.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); - await contentItemIndexHandlers.InvokeAsync(x => x.BuildIndexAsync(buildIndexContext), logger); - - await luceneIndexManager.DeleteDocumentsAsync(indexSettings.IndexName, new string[] { contentItem.ContentItemId }); - await luceneIndexManager.StoreDocumentsAsync(indexSettings.IndexName, new DocumentIndex[] { buildIndexContext.DocumentIndex }); - } + var buildIndexContext = new BuildIndexContext(new DocumentIndex(contentItem.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); + await contentItemIndexHandlers.InvokeAsync(x => x.BuildIndexAsync(buildIndexContext), logger); + + await luceneIndexManager.DeleteDocumentsAsync(indexSettings.IndexName, new string[] { contentItem.ContentItemId }); + await luceneIndexManager.StoreDocumentsAsync(indexSettings.IndexName, new DocumentIndex[] { buildIndexContext.DocumentIndex }); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Migrations.cs index d0b635136a8..291a1fefee6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Migrations.cs @@ -13,87 +13,42 @@ using OrchardCore.Search.Lucene.Model; using YesSql; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ShellDescriptor _shellDescriptor; + + public Migrations( + IContentDefinitionManager contentDefinitionManager, + ShellDescriptor shellDescriptor) { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ShellDescriptor _shellDescriptor; + _contentDefinitionManager = contentDefinitionManager; + _shellDescriptor = shellDescriptor; + } - public Migrations( - IContentDefinitionManager contentDefinitionManager, - ShellDescriptor shellDescriptor) + // New installations don't need to be upgraded, but because there is no initial migration record, + // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. + public async Task CreateAsync() + { + if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.Search.Lucene")) { - _contentDefinitionManager = contentDefinitionManager; - _shellDescriptor = shellDescriptor; + await UpgradeAsync(); } - // New installations don't need to be upgraded, but because there is no initial migration record, - // 'UpgradeAsync()' is called in a new 'CreateAsync()' but only if the feature was already installed. - public async Task CreateAsync() - { - if (_shellDescriptor.WasFeatureAlreadyInstalled("OrchardCore.Search.Lucene")) - { - await UpgradeAsync(); - } + // Shortcut other migration steps on new content definition schemas. + return 1; + } - // Shortcut other migration steps on new content definition schemas. - return 1; - } + // Upgrade an existing installation. + private async Task UpgradeAsync() + { + var contentTypeDefinitions = await _contentDefinitionManager.LoadTypeDefinitionsAsync(); - // Upgrade an existing installation. - private async Task UpgradeAsync() + foreach (var contentTypeDefinition in contentTypeDefinitions) { - var contentTypeDefinitions = await _contentDefinitionManager.LoadTypeDefinitionsAsync(); - - foreach (var contentTypeDefinition in contentTypeDefinitions) - { - foreach (var partDefinition in contentTypeDefinition.Parts) - { - await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => - { - if (partDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingPartSettings) && - !partDefinition.Settings.ContainsKey(nameof(LuceneContentIndexSettings))) - { - var included = existingPartSettings["Included"]; - var analyzed = existingPartSettings["Analyzed"]; - - if (included is not null) - { - if (analyzed is not null) - { - if ((bool)included && !(bool)analyzed) - { - existingPartSettings["Keyword"] = true; - } - } - else - { - if ((bool)included) - { - existingPartSettings["Keyword"] = true; - } - } - } - - var jExistingPartSettings = existingPartSettings.AsObject(); - - // We remove unnecessary properties from old releases. - jExistingPartSettings.Remove("Analyzed"); - jExistingPartSettings.Remove("Tokenized"); - jExistingPartSettings.Remove("Template"); - - partDefinition.Settings.Add(nameof(LuceneContentIndexSettings), jExistingPartSettings.Clone()); - } - - partDefinition.Settings.Remove("ContentIndexSettings"); - }); - } - } - - var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); - - foreach (var partDefinition in partDefinitions) + foreach (var partDefinition in contentTypeDefinition.Parts) { await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => { @@ -103,9 +58,9 @@ await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, pa var included = existingPartSettings["Included"]; var analyzed = existingPartSettings["Analyzed"]; - if (included != null) + if (included is not null) { - if (analyzed != null) + if (analyzed is not null) { if ((bool)included && !(bool)analyzed) { @@ -132,106 +87,150 @@ await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, pa } partDefinition.Settings.Remove("ContentIndexSettings"); + }); + } + } - foreach (var fieldDefinition in partDefinition.Fields) + var partDefinitions = await _contentDefinitionManager.LoadPartDefinitionsAsync(); + + foreach (var partDefinition in partDefinitions) + { + await _contentDefinitionManager.AlterPartDefinitionAsync(partDefinition.Name, partBuilder => + { + if (partDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingPartSettings) && + !partDefinition.Settings.ContainsKey(nameof(LuceneContentIndexSettings))) + { + var included = existingPartSettings["Included"]; + var analyzed = existingPartSettings["Analyzed"]; + + if (included != null) { - if (fieldDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingFieldSettings) && - !fieldDefinition.Settings.TryGetPropertyValue(nameof(LuceneContentIndexSettings), out _)) + if (analyzed != null) { - var included = existingFieldSettings["Included"]; - var analyzed = existingFieldSettings["Analyzed"]; + if ((bool)included && !(bool)analyzed) + { + existingPartSettings["Keyword"] = true; + } + } + else + { + if ((bool)included) + { + existingPartSettings["Keyword"] = true; + } + } + } + + var jExistingPartSettings = existingPartSettings.AsObject(); + + // We remove unnecessary properties from old releases. + jExistingPartSettings.Remove("Analyzed"); + jExistingPartSettings.Remove("Tokenized"); + jExistingPartSettings.Remove("Template"); + + partDefinition.Settings.Add(nameof(LuceneContentIndexSettings), jExistingPartSettings.Clone()); + } + + partDefinition.Settings.Remove("ContentIndexSettings"); + + foreach (var fieldDefinition in partDefinition.Fields) + { + if (fieldDefinition.Settings.TryGetPropertyValue("ContentIndexSettings", out var existingFieldSettings) && + !fieldDefinition.Settings.TryGetPropertyValue(nameof(LuceneContentIndexSettings), out _)) + { + var included = existingFieldSettings["Included"]; + var analyzed = existingFieldSettings["Analyzed"]; - if (included != null) + if (included != null) + { + if (analyzed != null) { - if (analyzed != null) + if ((bool)included && !(bool)analyzed) { - if ((bool)included && !(bool)analyzed) - { - existingFieldSettings["Keyword"] = true; - } + existingFieldSettings["Keyword"] = true; } - else + } + else + { + if ((bool)included) { - if ((bool)included) - { - existingFieldSettings["Keyword"] = true; - } + existingFieldSettings["Keyword"] = true; } } + } - var jExistingFieldSettings = existingFieldSettings.AsObject(); - - // We remove unnecessary properties from old releases. - jExistingFieldSettings.Remove("Analyzed"); - jExistingFieldSettings.Remove("Tokenized"); - jExistingFieldSettings.Remove("Template"); + var jExistingFieldSettings = existingFieldSettings.AsObject(); - fieldDefinition.Settings.Add(nameof(LuceneContentIndexSettings), jExistingFieldSettings.Clone()); - } + // We remove unnecessary properties from old releases. + jExistingFieldSettings.Remove("Analyzed"); + jExistingFieldSettings.Remove("Tokenized"); + jExistingFieldSettings.Remove("Template"); - fieldDefinition.Settings.Remove("ContentIndexSettings"); + fieldDefinition.Settings.Add(nameof(LuceneContentIndexSettings), jExistingFieldSettings.Clone()); } - }); - } - // Defer this until after the subsequent migrations have succeeded as the schema has changed. - ShellScope.AddDeferredTask(async scope => - { - var session = scope.ServiceProvider.GetRequiredService(); - var dbConnectionAccessor = scope.ServiceProvider.GetService(); - var logger = scope.ServiceProvider.GetService>(); - var tablePrefix = session.Store.Configuration.TablePrefix; - var documentTableName = session.Store.Configuration.TableNameConvention.GetDocumentTable(); - var table = $"{session.Store.Configuration.TablePrefix}{documentTableName}"; + fieldDefinition.Settings.Remove("ContentIndexSettings"); + } + }); + } - await using var connection = dbConnectionAccessor.CreateConnection(); - await connection.OpenAsync(); + // Defer this until after the subsequent migrations have succeeded as the schema has changed. + ShellScope.AddDeferredTask(async scope => + { + var session = scope.ServiceProvider.GetRequiredService(); + var dbConnectionAccessor = scope.ServiceProvider.GetService(); + var logger = scope.ServiceProvider.GetService>(); + var tablePrefix = session.Store.Configuration.TablePrefix; + var documentTableName = session.Store.Configuration.TableNameConvention.GetDocumentTable(); + var table = $"{session.Store.Configuration.TablePrefix}{documentTableName}"; - using var transaction = await connection.BeginTransactionAsync(session.Store.Configuration.IsolationLevel); - var dialect = session.Store.Configuration.SqlDialect; + await using var connection = dbConnectionAccessor.CreateConnection(); + await connection.OpenAsync(); - try - { - logger.LogDebug("Updating Lucene indices settings and queries"); + using var transaction = await connection.BeginTransactionAsync(session.Store.Configuration.IsolationLevel); + var dialect = session.Store.Configuration.SqlDialect; - var quotedTableName = dialect.QuoteForTableName(table, session.Store.Configuration.Schema); - var quotedContentColumnName = dialect.QuoteForColumnName("Content"); - var quotedTypeColumnName = dialect.QuoteForColumnName("Type"); + try + { + logger.LogDebug("Updating Lucene indices settings and queries"); - var updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.LuceneQuery, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.LuceneQuery, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Queries.Services.QueriesDocument, OrchardCore.Queries'"; + var quotedTableName = dialect.QuoteForTableName(table, session.Store.Configuration.Schema); + var quotedContentColumnName = dialect.QuoteForColumnName("Content"); + var quotedTypeColumnName = dialect.QuoteForColumnName("Type"); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + var updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.LuceneQuery, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.LuceneQuery, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Queries.Services.QueriesDocument, OrchardCore.Queries'"; - updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; - updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneSettingsDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneSettingsDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneSettingsDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneSettingsDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; - updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexResetDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexResetDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexResetDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexResetDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; - updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexRebuildDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexRebuildDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + updateCmd = $"UPDATE {quotedTableName} SET {quotedContentColumnName} = REPLACE({quotedContentColumnName}, '\"$type\":\"OrchardCore.Lucene.Deployment.LuceneIndexRebuildDeploymentStep, OrchardCore.Lucene\"', '\"$type\":\"OrchardCore.Search.Lucene.Deployment.LuceneIndexRebuildDeploymentStep, OrchardCore.Search.Lucene\"') WHERE {quotedTypeColumnName} = 'OrchardCore.Deployment.DeploymentPlan, OrchardCore.Deployment.Abstractions'"; - updateCmd = $"UPDATE {quotedTableName} SET {quotedTypeColumnName} = 'OrchardCore.Search.Lucene.Model.LuceneIndexSettingsDocument, OrchardCore.Search.Lucene' WHERE {quotedTypeColumnName} = 'OrchardCore.Lucene.Model.LuceneIndexSettingsDocument, OrchardCore.Lucene'"; + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); + updateCmd = $"UPDATE {quotedTableName} SET {quotedTypeColumnName} = 'OrchardCore.Search.Lucene.Model.LuceneIndexSettingsDocument, OrchardCore.Search.Lucene' WHERE {quotedTypeColumnName} = 'OrchardCore.Lucene.Model.LuceneIndexSettingsDocument, OrchardCore.Lucene'"; - await transaction.CommitAsync(); - } - catch (Exception e) - { - await transaction.RollbackAsync(); - logger.LogError(e, "An error occurred while updating Lucene indices settings and queries"); + await transaction.Connection.ExecuteAsync(updateCmd, null, transaction); - throw; - } - }); - } + await transaction.CommitAsync(); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + logger.LogError(e, "An error occurred while updating Lucene indices settings and queries"); + + throw; + } + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneContentIndexSettings.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneContentIndexSettings.cs index ef7494bb135..0ee7502c240 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneContentIndexSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneContentIndexSettings.cs @@ -1,31 +1,30 @@ using OrchardCore.Indexing; -namespace OrchardCore.Search.Lucene.Model +namespace OrchardCore.Search.Lucene.Model; + +/// +/// Represents the indexing settings for a content part or a field. +/// +public class LuceneContentIndexSettings : IContentIndexSettings { - /// - /// Represents the indexing settings for a content part or a field. - /// - public class LuceneContentIndexSettings : IContentIndexSettings + public bool Included { get; set; } + public bool Stored { get; set; } + public bool Keyword { get; set; } + + public DocumentIndexOptions ToOptions() { - public bool Included { get; set; } - public bool Stored { get; set; } - public bool Keyword { get; set; } + var options = DocumentIndexOptions.None; - public DocumentIndexOptions ToOptions() + if (Stored) { - var options = DocumentIndexOptions.None; - - if (Stored) - { - options |= DocumentIndexOptions.Store; - } - - if (Keyword) - { - options |= DocumentIndexOptions.Keyword; - } + options |= DocumentIndexOptions.Store; + } - return options; + if (Keyword) + { + options |= DocumentIndexOptions.Keyword; } + + return options; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneIndexSettings.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneIndexSettings.cs index bd3f93f64ad..ae7bad3605f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneIndexSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneIndexSettings.cs @@ -2,26 +2,25 @@ using System.Text.Json.Serialization; using OrchardCore.Data.Documents; -namespace OrchardCore.Search.Lucene.Model +namespace OrchardCore.Search.Lucene.Model; + +public class LuceneIndexSettings { - public class LuceneIndexSettings - { - [JsonIgnore] - public string IndexName { get; set; } + [JsonIgnore] + public string IndexName { get; set; } - public string AnalyzerName { get; set; } + public string AnalyzerName { get; set; } - public bool IndexLatest { get; set; } + public bool IndexLatest { get; set; } - public string[] IndexedContentTypes { get; set; } + public string[] IndexedContentTypes { get; set; } - public string Culture { get; set; } + public string Culture { get; set; } - public bool StoreSourceData { get; set; } - } + public bool StoreSourceData { get; set; } +} - public class LuceneIndexSettingsDocument : Document - { - public Dictionary LuceneIndexSettings { get; set; } = []; - } +public class LuceneIndexSettingsDocument : Document +{ + public Dictionary LuceneIndexSettings { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneQueryModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneQueryModel.cs index 58ee62a75e1..c323040b425 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneQueryModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneQueryModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Lucene.Model +namespace OrchardCore.Search.Lucene.Model; + +public class LuceneQueryModel { - public class LuceneQueryModel - { - public string IndexName { set; get; } - public string Query { set; get; } - public string Parameters { set; get; } - } + public string IndexName { set; get; } + public string Query { set; get; } + public string Parameters { set; get; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneSettings.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneSettings.cs index d040dd82dc4..d966a361b3f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Model/LuceneSettings.cs @@ -1,20 +1,19 @@ using Lucene.Net.Util; using OrchardCore.Contents.Indexing; -namespace OrchardCore.Search.Lucene.Model +namespace OrchardCore.Search.Lucene.Model; + +public class LuceneSettings { - public class LuceneSettings - { - public const string StandardAnalyzer = "standardanalyzer"; + public const string StandardAnalyzer = "standardanalyzer"; - public static readonly string[] FullTextField = [IndexingConstants.FullTextKey]; + public static readonly string[] FullTextField = [IndexingConstants.FullTextKey]; - public static readonly LuceneVersion DefaultVersion = LuceneVersion.LUCENE_48; + public static readonly LuceneVersion DefaultVersion = LuceneVersion.LUCENE_48; - public string SearchIndex { get; set; } + public string SearchIndex { get; set; } - public string[] DefaultSearchFields { get; set; } = FullTextField; + public string[] DefaultSearchFields { get; set; } = FullTextField; - public bool AllowLuceneQueriesInSearch { get; set; } - } + public bool AllowLuceneQueriesInSearch { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexRebuildStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexRebuildStep.cs index 0bd2542d876..e5c1871c552 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexRebuildStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexRebuildStep.cs @@ -7,50 +7,49 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Search.Lucene.Recipes +namespace OrchardCore.Search.Lucene.Recipes; + +/// +/// This recipe step rebuilds a Lucene index. +/// +public sealed class LuceneIndexRebuildStep : IRecipeStepHandler { - /// - /// This recipe step rebuilds a Lucene index. - /// - public sealed class LuceneIndexRebuildStep : IRecipeStepHandler + public async Task ExecuteAsync(RecipeExecutionContext context) { - public async Task ExecuteAsync(RecipeExecutionContext context) + if (!string.Equals(context.Name, "lucene-index-rebuild", StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(context.Name, "lucene-index-rebuild", StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - var model = context.Step.ToObject(); + var model = context.Step.ToObject(); - if (model.IncludeAll || model.Indices.Length > 0) + if (model.IncludeAll || model.Indices.Length > 0) + { + await HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("lucene-index-rebuild", async (scope) => { - await HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("lucene-index-rebuild", async (scope) => - { - var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); - var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); + var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); + var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); - var indices = model.IncludeAll - ? (await luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray() - : model.Indices; + var indices = model.IncludeAll + ? (await luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray() + : model.Indices; - foreach (var indexName in indices) + foreach (var indexName in indices) + { + var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(indexName); + if (luceneIndexSettings != null) { - var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(indexName); - if (luceneIndexSettings != null) - { - await luceneIndexingService.RebuildIndexAsync(indexName); - await luceneIndexingService.ProcessContentItemsAsync(indexName); - } + await luceneIndexingService.RebuildIndexAsync(indexName); + await luceneIndexingService.ProcessContentItemsAsync(indexName); } - }); - } + } + }); } + } - private sealed class LuceneIndexRebuildStepModel - { - public bool IncludeAll { get; set; } - public string[] Indices { get; set; } = []; - } + private sealed class LuceneIndexRebuildStepModel + { + public bool IncludeAll { get; set; } + public string[] Indices { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexResetStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexResetStep.cs index 89e4b359ce6..cd9f9356c90 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexResetStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexResetStep.cs @@ -7,58 +7,57 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Search.Lucene.Recipes +namespace OrchardCore.Search.Lucene.Recipes; + +/// +/// This recipe step resets a lucene index. +/// +public sealed class LuceneIndexResetStep : IRecipeStepHandler { - /// - /// This recipe step resets a lucene index. - /// - public sealed class LuceneIndexResetStep : IRecipeStepHandler + public async Task ExecuteAsync(RecipeExecutionContext context) { - public async Task ExecuteAsync(RecipeExecutionContext context) + if (!string.Equals(context.Name, "lucene-index-reset", StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(context.Name, "lucene-index-reset", StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - var model = context.Step.ToObject(); + var model = context.Step.ToObject(); - if (model.IncludeAll || model.Indices.Length > 0) + if (model.IncludeAll || model.Indices.Length > 0) + { + await HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("lucene-index-reset", async (scope) => { - await HttpBackgroundJob.ExecuteAfterEndOfRequestAsync("lucene-index-reset", async (scope) => - { - var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); - var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); - var luceneIndexManager = scope.ServiceProvider.GetRequiredService(); + var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); + var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); + var luceneIndexManager = scope.ServiceProvider.GetRequiredService(); - var indices = model.IncludeAll - ? (await luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray() - : model.Indices; + var indices = model.IncludeAll + ? (await luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray() + : model.Indices; - foreach (var indexName in indices) + foreach (var indexName in indices) + { + var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(indexName); + if (luceneIndexSettings != null) { - var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(indexName); - if (luceneIndexSettings != null) + if (!luceneIndexManager.Exists(indexName)) { - if (!luceneIndexManager.Exists(indexName)) - { - await luceneIndexingService.CreateIndexAsync(luceneIndexSettings); - } - else - { - luceneIndexingService.ResetIndexAsync(indexName); - } - await luceneIndexingService.ProcessContentItemsAsync(indexName); + await luceneIndexingService.CreateIndexAsync(luceneIndexSettings); } + else + { + luceneIndexingService.ResetIndexAsync(indexName); + } + await luceneIndexingService.ProcessContentItemsAsync(indexName); } - }); - } + } + }); } + } - private sealed class LuceneIndexResetStepModel - { - public bool IncludeAll { get; set; } - public string[] Indices { get; set; } = []; - } + private sealed class LuceneIndexResetStepModel + { + public bool IncludeAll { get; set; } + public string[] Indices { get; set; } = []; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexStep.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexStep.cs index e1e6b1acc30..11bbe64ffa0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Recipes/LuceneIndexStep.cs @@ -7,52 +7,51 @@ using OrchardCore.Recipes.Services; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene.Recipes +namespace OrchardCore.Search.Lucene.Recipes; + +/// +/// This recipe step creates a lucene index. +/// +public sealed class LuceneIndexStep : IRecipeStepHandler { - /// - /// This recipe step creates a lucene index. - /// - public sealed class LuceneIndexStep : IRecipeStepHandler + private readonly LuceneIndexingService _luceneIndexingService; + private readonly LuceneIndexManager _luceneIndexManager; + + public LuceneIndexStep( + LuceneIndexingService luceneIndexingService, + LuceneIndexManager luceneIndexManager + ) { - private readonly LuceneIndexingService _luceneIndexingService; - private readonly LuceneIndexManager _luceneIndexManager; + _luceneIndexManager = luceneIndexManager; + _luceneIndexingService = luceneIndexingService; + } - public LuceneIndexStep( - LuceneIndexingService luceneIndexingService, - LuceneIndexManager luceneIndexManager - ) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "lucene-index", StringComparison.OrdinalIgnoreCase)) { - _luceneIndexManager = luceneIndexManager; - _luceneIndexingService = luceneIndexingService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + if (context.Step["Indices"] is not JsonArray jsonArray) { - if (!string.Equals(context.Name, "lucene-index", StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - if (context.Step["Indices"] is not JsonArray jsonArray) - { - return; - } + foreach (var index in jsonArray) + { + var luceneIndexSettings = index.ToObject>().FirstOrDefault(); - foreach (var index in jsonArray) + if (!_luceneIndexManager.Exists(luceneIndexSettings.Key)) { - var luceneIndexSettings = index.ToObject>().FirstOrDefault(); - - if (!_luceneIndexManager.Exists(luceneIndexSettings.Key)) - { - luceneIndexSettings.Value.IndexName = luceneIndexSettings.Key; - await _luceneIndexingService.CreateIndexAsync(luceneIndexSettings.Value); - } + luceneIndexSettings.Value.IndexName = luceneIndexSettings.Key; + await _luceneIndexingService.CreateIndexAsync(luceneIndexSettings.Value); } } } +} - public sealed class ContentStepModel - { - public JsonObject Data { get; set; } - } +public sealed class ContentStepModel +{ + public JsonObject Data { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/IndexingBackgroundTask.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/IndexingBackgroundTask.cs index e31971be76e..91698ffe5f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/IndexingBackgroundTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/IndexingBackgroundTask.cs @@ -4,24 +4,23 @@ using Microsoft.Extensions.DependencyInjection; using OrchardCore.BackgroundTasks; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +/// +/// This background task will index content items using Lucene. +/// +/// +/// This services is only registered from OrchardCore.Search.Lucene.Worker feature. +/// +[BackgroundTask( + Title = "Lucene Indexes Updater", + Schedule = "* * * * *", + Description = "Updates lucene indexes.")] +public sealed class IndexingBackgroundTask : IBackgroundTask { - /// - /// This background task will index content items using Lucene. - /// - /// - /// This services is only registered from OrchardCore.Search.Lucene.Worker feature. - /// - [BackgroundTask( - Title = "Lucene Indexes Updater", - Schedule = "* * * * *", - Description = "Updates lucene indexes.")] - public sealed class IndexingBackgroundTask : IBackgroundTask + public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) { - public Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) - { - var indexingService = serviceProvider.GetService(); - return indexingService.ProcessContentItemsAsync(); - } + var indexingService = serviceProvider.GetService(); + return indexingService.ProcessContentItemsAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzer.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzer.cs index 1ab4662d7d3..645c732b8d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzer.cs @@ -1,26 +1,25 @@ using System; using Lucene.Net.Analysis; -namespace OrchardCore.Search.Lucene.Services +namespace OrchardCore.Search.Lucene.Services; + +public class LuceneAnalyzer : ILuceneAnalyzer { - public class LuceneAnalyzer : ILuceneAnalyzer - { - private readonly Func _factory; + private readonly Func _factory; - public LuceneAnalyzer(string name, Func factory) - { - _factory = factory; - Name = name; - } + public LuceneAnalyzer(string name, Func factory) + { + _factory = factory; + Name = name; + } - public LuceneAnalyzer(string name, Analyzer instance) : this(name, () => instance) - { - } + public LuceneAnalyzer(string name, Analyzer instance) : this(name, () => instance) + { + } - public string Name { get; } - public Analyzer CreateAnalyzer() - { - return _factory(); - } + public string Name { get; } + public Analyzer CreateAnalyzer() + { + return _factory(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzerManager.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzerManager.cs index 55135882eb2..307dd58d997 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzerManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneAnalyzerManager.cs @@ -3,39 +3,38 @@ using Lucene.Net.Analysis; using Microsoft.Extensions.Options; -namespace OrchardCore.Search.Lucene.Services +namespace OrchardCore.Search.Lucene.Services; + +/// +/// Coordinates implementations provided by +/// to return the list of all available objects. +/// +public class LuceneAnalyzerManager { - /// - /// Coordinates implementations provided by - /// to return the list of all available objects. - /// - public class LuceneAnalyzerManager + private readonly Dictionary _analyzers; + + public LuceneAnalyzerManager(IOptions options) { - private readonly Dictionary _analyzers; + _analyzers = new Dictionary(StringComparer.OrdinalIgnoreCase); - public LuceneAnalyzerManager(IOptions options) + foreach (var analyzer in options.Value.Analyzers) { - _analyzers = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var analyzer in options.Value.Analyzers) - { - _analyzers[analyzer.Name] = analyzer; - } + _analyzers[analyzer.Name] = analyzer; } + } - public IEnumerable GetAnalyzers() - { - return _analyzers.Values; - } + public IEnumerable GetAnalyzers() + { + return _analyzers.Values; + } - public Analyzer CreateAnalyzer(string name) + public Analyzer CreateAnalyzer(string name) + { + if (_analyzers.TryGetValue(name, out var analyzer)) { - if (_analyzers.TryGetValue(name, out var analyzer)) - { - return analyzer.CreateAnalyzer(); - } - - return null; + return analyzer.CreateAnalyzer(); } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneContentPickerResultProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneContentPickerResultProvider.cs index 18592a9f90c..adeceb4d997 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneContentPickerResultProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneContentPickerResultProvider.cs @@ -7,70 +7,69 @@ using OrchardCore.ContentManagement; using OrchardCore.Search.Lucene.Settings; -namespace OrchardCore.Search.Lucene.Services +namespace OrchardCore.Search.Lucene.Services; + +public class LuceneContentPickerResultProvider : IContentPickerResultProvider { - public class LuceneContentPickerResultProvider : IContentPickerResultProvider + private readonly LuceneIndexManager _luceneIndexManager; + + public LuceneContentPickerResultProvider(LuceneIndexManager luceneIndexManager) + { + _luceneIndexManager = luceneIndexManager; + } + + public string Name => "Lucene"; + + public async Task> Search(ContentPickerSearchContext searchContext) { - private readonly LuceneIndexManager _luceneIndexManager; + var indexName = "Search"; - public LuceneContentPickerResultProvider(LuceneIndexManager luceneIndexManager) + var fieldSettings = searchContext.PartFieldDefinition?.GetSettings(); + + if (!string.IsNullOrWhiteSpace(fieldSettings?.Index)) { - _luceneIndexManager = luceneIndexManager; + indexName = fieldSettings.Index; } - public string Name => "Lucene"; - - public async Task> Search(ContentPickerSearchContext searchContext) + if (!_luceneIndexManager.Exists(indexName)) { - var indexName = "Search"; + return []; + } - var fieldSettings = searchContext.PartFieldDefinition?.GetSettings(); + var results = new List(); - if (!string.IsNullOrWhiteSpace(fieldSettings?.Index)) - { - indexName = fieldSettings.Index; - } + await _luceneIndexManager.SearchAsync(indexName, searcher => + { + Query query = null; - if (!_luceneIndexManager.Exists(indexName)) + if (string.IsNullOrWhiteSpace(searchContext.Query)) { - return []; + query = new MatchAllDocsQuery(); } - - var results = new List(); - - await _luceneIndexManager.SearchAsync(indexName, searcher => + else { - Query query = null; + query = new WildcardQuery(new Term("Content.ContentItem.DisplayText.Normalized", searchContext.Query.ToLowerInvariant() + "*")); + } - if (string.IsNullOrWhiteSpace(searchContext.Query)) - { - query = new MatchAllDocsQuery(); - } - else - { - query = new WildcardQuery(new Term("Content.ContentItem.DisplayText.Normalized", searchContext.Query.ToLowerInvariant() + "*")); - } + var filter = new FieldCacheTermsFilter("Content.ContentItem.ContentType", searchContext.ContentTypes.ToArray()); - var filter = new FieldCacheTermsFilter("Content.ContentItem.ContentType", searchContext.ContentTypes.ToArray()); + var docs = searcher.Search(query, filter, 50, Sort.RELEVANCE); - var docs = searcher.Search(query, filter, 50, Sort.RELEVANCE); + foreach (var hit in docs.ScoreDocs) + { + var doc = searcher.Doc(hit.Doc); - foreach (var hit in docs.ScoreDocs) + results.Add(new ContentPickerResult { - var doc = searcher.Doc(hit.Doc); - - results.Add(new ContentPickerResult - { - ContentItemId = doc.GetField("ContentItemId").GetStringValue(), - DisplayText = doc.GetField("Content.ContentItem.DisplayText.keyword").GetStringValue(), - HasPublished = doc.GetField("Content.ContentItem.Published").GetStringValue().Equals("true", StringComparison.OrdinalIgnoreCase), - }); - } + ContentItemId = doc.GetField("ContentItemId").GetStringValue(), + DisplayText = doc.GetField("Content.ContentItem.DisplayText.keyword").GetStringValue(), + HasPublished = doc.GetField("Content.ContentItem.Published").GetStringValue().Equals("true", StringComparison.OrdinalIgnoreCase), + }); + } - return Task.CompletedTask; - }); + return Task.CompletedTask; + }); - return results.OrderBy(x => x.DisplayText); - } + return results.OrderBy(x => x.DisplayText); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexInitializerService.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexInitializerService.cs index daa6e929e0f..4267358de11 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexInitializerService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexInitializerService.cs @@ -4,41 +4,40 @@ using OrchardCore.Environment.Shell.Scope; using OrchardCore.Modules; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +public class LuceneIndexInitializerService : ModularTenantEvents { - public class LuceneIndexInitializerService : ModularTenantEvents - { - private readonly ShellSettings _shellSettings; + private readonly ShellSettings _shellSettings; - public LuceneIndexInitializerService(ShellSettings shellSettings) - { - _shellSettings = shellSettings; - } + public LuceneIndexInitializerService(ShellSettings shellSettings) + { + _shellSettings = shellSettings; + } - public override Task ActivatedAsync() + public override Task ActivatedAsync() + { + if (_shellSettings.IsRunning()) { - if (_shellSettings.IsRunning()) + ShellScope.AddDeferredTask(async scope => { - ShellScope.AddDeferredTask(async scope => - { - var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); - var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); - var indexManager = scope.ServiceProvider.GetRequiredService(); + var luceneIndexSettingsService = scope.ServiceProvider.GetRequiredService(); + var luceneIndexingService = scope.ServiceProvider.GetRequiredService(); + var indexManager = scope.ServiceProvider.GetRequiredService(); - var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(); + var luceneIndexSettings = await luceneIndexSettingsService.GetSettingsAsync(); - foreach (var settings in luceneIndexSettings) + foreach (var settings in luceneIndexSettings) + { + if (!indexManager.Exists(settings.IndexName)) { - if (!indexManager.Exists(settings.IndexName)) - { - await luceneIndexingService.CreateIndexAsync(settings); - await luceneIndexingService.ProcessContentItemsAsync(settings.IndexName); - } + await luceneIndexingService.CreateIndexAsync(settings); + await luceneIndexingService.ProcessContentItemsAsync(settings.IndexName); } - }); - } - - return Task.CompletedTask; + } + }); } + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexManager.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexManager.cs index 50d1c3434db..5617a9d330b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexManager.cs @@ -24,564 +24,563 @@ using LDirectory = Lucene.Net.Store.Directory; using LuceneLock = Lucene.Net.Store.Lock; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +/// +/// Provides methods to manage physical Lucene indices. +/// This class is provided as a singleton to that the index searcher can be reused across requests. +/// +public class LuceneIndexManager : IDisposable { - /// - /// Provides methods to manage physical Lucene indices. - /// This class is provided as a singleton to that the index searcher can be reused across requests. - /// - public class LuceneIndexManager : IDisposable + private readonly IClock _clock; + private readonly ILogger _logger; + private readonly string _rootPath; + private bool _disposed; + private readonly ConcurrentDictionary _indexPools = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _writers = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _timestamps = new(StringComparer.OrdinalIgnoreCase); + private readonly LuceneAnalyzerManager _luceneAnalyzerManager; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly SpatialContext _ctx; + private readonly GeohashPrefixTree _grid; + private readonly static object _synLock = new(); + + public LuceneIndexManager( + IClock clock, + IOptions shellOptions, + ShellSettings shellSettings, + ILogger logger, + LuceneAnalyzerManager luceneAnalyzerManager, + LuceneIndexSettingsService luceneIndexSettingsService + ) { - private readonly IClock _clock; - private readonly ILogger _logger; - private readonly string _rootPath; - private bool _disposed; - private readonly ConcurrentDictionary _indexPools = new(StringComparer.OrdinalIgnoreCase); - private readonly ConcurrentDictionary _writers = new(StringComparer.OrdinalIgnoreCase); - private readonly ConcurrentDictionary _timestamps = new(StringComparer.OrdinalIgnoreCase); - private readonly LuceneAnalyzerManager _luceneAnalyzerManager; - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - private readonly SpatialContext _ctx; - private readonly GeohashPrefixTree _grid; - private readonly static object _synLock = new(); - - public LuceneIndexManager( - IClock clock, - IOptions shellOptions, - ShellSettings shellSettings, - ILogger logger, - LuceneAnalyzerManager luceneAnalyzerManager, - LuceneIndexSettingsService luceneIndexSettingsService - ) - { - _clock = clock; - _logger = logger; - _rootPath = PathExtensions.Combine( - shellOptions.Value.ShellsApplicationDataPath, - shellOptions.Value.ShellsContainerName, - shellSettings.Name, "Lucene"); - Directory.CreateDirectory(_rootPath); - _luceneAnalyzerManager = luceneAnalyzerManager; - _luceneIndexSettingsService = luceneIndexSettingsService; - - // Typical geospatial context. - // These can also be constructed from SpatialContextFactory. - _ctx = SpatialContext.Geo; - - var maxLevels = 11; // Results in sub-meter precision for geohash. - - // TODO demo lookup by detail distance. - // This can also be constructed from SpatialPrefixTreeFactory. - _grid = new GeohashPrefixTree(_ctx, maxLevels); - } + _clock = clock; + _logger = logger; + _rootPath = PathExtensions.Combine( + shellOptions.Value.ShellsApplicationDataPath, + shellOptions.Value.ShellsContainerName, + shellSettings.Name, "Lucene"); + Directory.CreateDirectory(_rootPath); + _luceneAnalyzerManager = luceneAnalyzerManager; + _luceneIndexSettingsService = luceneIndexSettingsService; + + // Typical geospatial context. + // These can also be constructed from SpatialContextFactory. + _ctx = SpatialContext.Geo; + + var maxLevels = 11; // Results in sub-meter precision for geohash. + + // TODO demo lookup by detail distance. + // This can also be constructed from SpatialPrefixTreeFactory. + _grid = new GeohashPrefixTree(_ctx, maxLevels); + } - public async Task CreateIndexAsync(string indexName) - { - await WriteAsync(indexName, _ => { }, true); - } + public async Task CreateIndexAsync(string indexName) + { + await WriteAsync(indexName, _ => { }, true); + } - public async Task DeleteDocumentsAsync(string indexName, IEnumerable contentItemIds) + public async Task DeleteDocumentsAsync(string indexName, IEnumerable contentItemIds) + { + await WriteAsync(indexName, writer => { - await WriteAsync(indexName, writer => - { - writer.DeleteDocuments(contentItemIds.Select(x => new Term("ContentItemId", x)).ToArray()); + writer.DeleteDocuments(contentItemIds.Select(x => new Term("ContentItemId", x)).ToArray()); - writer.Commit(); + writer.Commit(); - if (_indexPools.TryRemove(indexName, out var pool)) - { - pool.MakeDirty(); - pool.Release(); - } - }); - } + if (_indexPools.TryRemove(indexName, out var pool)) + { + pool.MakeDirty(); + pool.Release(); + } + }); + } - public void DeleteIndex(string indexName) + public void DeleteIndex(string indexName) + { + lock (this) { - lock (this) + if (_writers.TryGetValue(indexName, out var writer)) { - if (_writers.TryGetValue(indexName, out var writer)) - { - writer.IsClosing = true; - writer.Dispose(); - } + writer.IsClosing = true; + writer.Dispose(); + } - if (_indexPools.TryRemove(indexName, out var reader)) - { - reader.Dispose(); - } + if (_indexPools.TryRemove(indexName, out var reader)) + { + reader.Dispose(); + } - _timestamps.TryRemove(indexName, out _); + _timestamps.TryRemove(indexName, out _); - var indexFolder = PathExtensions.Combine(_rootPath, indexName); + var indexFolder = PathExtensions.Combine(_rootPath, indexName); - if (Directory.Exists(indexFolder)) + if (Directory.Exists(indexFolder)) + { + try { - try - { - Directory.Delete(indexFolder, true); - } - catch { } + Directory.Delete(indexFolder, true); } - - _writers.TryRemove(indexName, out _); + catch { } } + + _writers.TryRemove(indexName, out _); } + } - public bool Exists(string indexName) + public bool Exists(string indexName) + { + if (string.IsNullOrWhiteSpace(indexName)) { - if (string.IsNullOrWhiteSpace(indexName)) - { - return false; - } - - return Directory.Exists(PathExtensions.Combine(_rootPath, indexName)); + return false; } - public async Task StoreDocumentsAsync(string indexName, IEnumerable indexDocuments) - { - var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); + return Directory.Exists(PathExtensions.Combine(_rootPath, indexName)); + } - await WriteAsync(indexName, writer => + public async Task StoreDocumentsAsync(string indexName, IEnumerable indexDocuments) + { + var luceneIndexSettings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); + + await WriteAsync(indexName, writer => + { + foreach (var indexDocument in indexDocuments) { - foreach (var indexDocument in indexDocuments) - { - writer.AddDocument(CreateLuceneDocument(indexDocument, luceneIndexSettings)); - } + writer.AddDocument(CreateLuceneDocument(indexDocument, luceneIndexSettings)); + } - writer.Commit(); + writer.Commit(); - if (_indexPools.TryRemove(indexName, out var pool)) - { - pool.MakeDirty(); - pool.Release(); - } - }); - } + if (_indexPools.TryRemove(indexName, out var pool)) + { + pool.MakeDirty(); + pool.Release(); + } + }); + } - public async Task SearchAsync(string indexName, Func searcher) + public async Task SearchAsync(string indexName, Func searcher) + { + if (Exists(indexName)) { - if (Exists(indexName)) + using (var reader = GetReader(indexName)) { - using (var reader = GetReader(indexName)) - { - var indexSearcher = new IndexSearcher(reader.IndexReader); - await searcher(indexSearcher); - } - - _timestamps[indexName] = _clock.UtcNow; + var indexSearcher = new IndexSearcher(reader.IndexReader); + await searcher(indexSearcher); } + + _timestamps[indexName] = _clock.UtcNow; } + } - public void Read(string indexName, Action reader) + public void Read(string indexName, Action reader) + { + if (Exists(indexName)) { - if (Exists(indexName)) + using (var indexReader = GetReader(indexName)) { - using (var indexReader = GetReader(indexName)) - { - reader(indexReader.IndexReader); - } - - _timestamps[indexName] = _clock.UtcNow; + reader(indexReader.IndexReader); } + + _timestamps[indexName] = _clock.UtcNow; } + } - private Document CreateLuceneDocument(DocumentIndex documentIndex, LuceneIndexSettings indexSettings) + private Document CreateLuceneDocument(DocumentIndex documentIndex, LuceneIndexSettings indexSettings) + { + var doc = new Document { - var doc = new Document - { - // Always store the content item id and version id. - // These fields need to be indexed as a StringField because it needs to be searchable for the writer.DeleteDocuments method. - // Else it won't be able to prune oldest draft from the indexes. - // Maybe eventually find a way to remove a document from a StoredDocument. - new StringField(IndexingConstants.ContentItemIdKey, documentIndex.ContentItemId.ToString(), Field.Store.YES), - new StringField(IndexingConstants.ContentItemVersionIdKey, documentIndex.ContentItemVersionId.ToString(), Field.Store.YES), - }; - - if (indexSettings.StoreSourceData) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + IndexingConstants.ContentItemIdKey, documentIndex.ContentItemId.ToString())); - doc.Add(new StoredField(IndexingConstants.SourceKey + IndexingConstants.ContentItemVersionIdKey, documentIndex.ContentItemVersionId.ToString())); - } + // Always store the content item id and version id. + // These fields need to be indexed as a StringField because it needs to be searchable for the writer.DeleteDocuments method. + // Else it won't be able to prune oldest draft from the indexes. + // Maybe eventually find a way to remove a document from a StoredDocument. + new StringField(IndexingConstants.ContentItemIdKey, documentIndex.ContentItemId.ToString(), Field.Store.YES), + new StringField(IndexingConstants.ContentItemVersionIdKey, documentIndex.ContentItemVersionId.ToString(), Field.Store.YES), + }; + + if (indexSettings.StoreSourceData) + { + doc.Add(new StoredField(IndexingConstants.SourceKey + IndexingConstants.ContentItemIdKey, documentIndex.ContentItemId.ToString())); + doc.Add(new StoredField(IndexingConstants.SourceKey + IndexingConstants.ContentItemVersionIdKey, documentIndex.ContentItemVersionId.ToString())); + } - foreach (var entry in documentIndex.Entries) + foreach (var entry in documentIndex.Entries) + { + var store = entry.Options.HasFlag(DocumentIndexOptions.Store) + ? Field.Store.YES + : Field.Store.NO; + + switch (entry.Type) { - var store = entry.Options.HasFlag(DocumentIndexOptions.Store) - ? Field.Store.YES - : Field.Store.NO; + case DocumentIndex.Types.Boolean: + // Store "true"/"false" for boolean. + doc.Add(new StringField(entry.Name, Convert.ToString(entry.Value).ToLowerInvariant(), store)); - switch (entry.Type) - { - case DocumentIndex.Types.Boolean: - // Store "true"/"false" for boolean. - doc.Add(new StringField(entry.Name, Convert.ToString(entry.Value).ToLowerInvariant(), store)); + if (indexSettings.StoreSourceData) + { + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, Convert.ToString(entry.Value).ToLowerInvariant())); + } + break; - if (indexSettings.StoreSourceData) + case DocumentIndex.Types.DateTime: + if (entry.Value != null) + { + if (entry.Value is DateTimeOffset) + { + doc.Add(new StringField(entry.Name, DateTools.DateToString(((DateTimeOffset)entry.Value).UtcDateTime, DateResolution.SECOND), store)); + } + else { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, Convert.ToString(entry.Value).ToLowerInvariant())); + doc.Add(new StringField(entry.Name, DateTools.DateToString(((DateTime)entry.Value).ToUniversalTime(), DateResolution.SECOND), store)); } - break; - case DocumentIndex.Types.DateTime: - if (entry.Value != null) + if (indexSettings.StoreSourceData) { if (entry.Value is DateTimeOffset) { - doc.Add(new StringField(entry.Name, DateTools.DateToString(((DateTimeOffset)entry.Value).UtcDateTime, DateResolution.SECOND), store)); + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, DateTools.DateToString(((DateTimeOffset)entry.Value).UtcDateTime, DateResolution.SECOND))); } else { - doc.Add(new StringField(entry.Name, DateTools.DateToString(((DateTime)entry.Value).ToUniversalTime(), DateResolution.SECOND), store)); - } - - if (indexSettings.StoreSourceData) - { - if (entry.Value is DateTimeOffset) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, DateTools.DateToString(((DateTimeOffset)entry.Value).UtcDateTime, DateResolution.SECOND))); - } - else - { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, DateTools.DateToString(((DateTime)entry.Value).ToUniversalTime(), DateResolution.SECOND))); - } + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, DateTools.DateToString(((DateTime)entry.Value).ToUniversalTime(), DateResolution.SECOND))); } } - else + } + else + { + doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + } + break; + + case DocumentIndex.Types.Integer: + if (entry.Value != null && long.TryParse(entry.Value.ToString(), out var value)) + { + doc.Add(new Int64Field(entry.Name, value, store)); + + if (indexSettings.StoreSourceData) { - doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, value)); } - break; + } + else + { + doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + } - case DocumentIndex.Types.Integer: - if (entry.Value != null && long.TryParse(entry.Value.ToString(), out var value)) - { - doc.Add(new Int64Field(entry.Name, value, store)); + break; - if (indexSettings.StoreSourceData) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, value)); - } - } - else + case DocumentIndex.Types.Number: + if (entry.Value != null) + { + doc.Add(new DoubleField(entry.Name, Convert.ToDouble(entry.Value), store)); + + if (indexSettings.StoreSourceData) { - doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, Convert.ToDouble(entry.Value))); } + } + else + { + doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + } + break; - break; + case DocumentIndex.Types.Text: + if (entry.Value != null && !string.IsNullOrEmpty(Convert.ToString(entry.Value))) + { + var stringValue = Convert.ToString(entry.Value); - case DocumentIndex.Types.Number: - if (entry.Value != null) + if (entry.Options.HasFlag(DocumentIndexOptions.Keyword)) { - doc.Add(new DoubleField(entry.Name, Convert.ToDouble(entry.Value), store)); - - if (indexSettings.StoreSourceData) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, Convert.ToDouble(entry.Value))); - } + doc.Add(new StringField(entry.Name, stringValue, store)); } else { - doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); + doc.Add(new TextField(entry.Name, stringValue, store)); } - break; - case DocumentIndex.Types.Text: - if (entry.Value != null && !string.IsNullOrEmpty(Convert.ToString(entry.Value))) + // This is for ElasticSearch Queries compatibility since a keyword field is always indexed + // by default when indexing without explicit mapping in ElasticSearch. + // Keyword ignore above 256 chars by default. + if (store == Field.Store.NO && !entry.Options.HasFlag(DocumentIndexOptions.Keyword) && stringValue.Length <= 256) { - var stringValue = Convert.ToString(entry.Value); - - if (entry.Options.HasFlag(DocumentIndexOptions.Keyword)) - { - doc.Add(new StringField(entry.Name, stringValue, store)); - } - else - { - doc.Add(new TextField(entry.Name, stringValue, store)); - } - - // This is for ElasticSearch Queries compatibility since a keyword field is always indexed - // by default when indexing without explicit mapping in ElasticSearch. - // Keyword ignore above 256 chars by default. - if (store == Field.Store.NO && !entry.Options.HasFlag(DocumentIndexOptions.Keyword) && stringValue.Length <= 256) - { - doc.Add(new StringField($"{entry.Name}.keyword", stringValue, Field.Store.NO)); - } + doc.Add(new StringField($"{entry.Name}.keyword", stringValue, Field.Store.NO)); + } - if (indexSettings.StoreSourceData) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, stringValue)); - } + if (indexSettings.StoreSourceData) + { + doc.Add(new StoredField(IndexingConstants.SourceKey + entry.Name, stringValue)); + } + } + else + { + if (entry.Options.HasFlag(DocumentIndexOptions.Keyword)) + { + doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); } else { - if (entry.Options.HasFlag(DocumentIndexOptions.Keyword)) - { - doc.Add(new StringField(entry.Name, IndexingConstants.NullValue, store)); - } - else - { - doc.Add(new TextField(entry.Name, IndexingConstants.NullValue, store)); - } + doc.Add(new TextField(entry.Name, IndexingConstants.NullValue, store)); } - break; + } + break; - case DocumentIndex.Types.GeoPoint: - var strategy = new RecursivePrefixTreeStrategy(_grid, entry.Name); + case DocumentIndex.Types.GeoPoint: + var strategy = new RecursivePrefixTreeStrategy(_grid, entry.Name); - if (entry.Value != null && entry.Value is DocumentIndex.GeoPoint point) + if (entry.Value != null && entry.Value is DocumentIndex.GeoPoint point) + { + var geoPoint = _ctx.MakePoint((double)point.Longitude, (double)point.Latitude); + foreach (var field in strategy.CreateIndexableFields(geoPoint)) { - var geoPoint = _ctx.MakePoint((double)point.Longitude, (double)point.Latitude); - foreach (var field in strategy.CreateIndexableFields(geoPoint)) - { - doc.Add(field); - } + doc.Add(field); + } - doc.Add(new StoredField(strategy.FieldName, $"{point.Latitude},{point.Longitude}")); + doc.Add(new StoredField(strategy.FieldName, $"{point.Latitude},{point.Longitude}")); - if (indexSettings.StoreSourceData) - { - doc.Add(new StoredField(IndexingConstants.SourceKey + strategy.FieldName, $"{point.Latitude},{point.Longitude}")); - } - } - else + if (indexSettings.StoreSourceData) { - doc.Add(new StoredField(strategy.FieldName, IndexingConstants.NullValue)); + doc.Add(new StoredField(IndexingConstants.SourceKey + strategy.FieldName, $"{point.Latitude},{point.Longitude}")); } - break; - } + } + else + { + doc.Add(new StoredField(strategy.FieldName, IndexingConstants.NullValue)); + } + break; } - - return doc; } - private FSDirectory CreateDirectory(string indexName) + return doc; + } + + private FSDirectory CreateDirectory(string indexName) + { + lock (this) { - lock (this) - { - var path = new DirectoryInfo(PathExtensions.Combine(_rootPath, indexName)); + var path = new DirectoryInfo(PathExtensions.Combine(_rootPath, indexName)); - if (!path.Exists) - { - path.Create(); - } + if (!path.Exists) + { + path.Create(); + } - // Lucene is not thread safe on this call. - lock (_synLock) - { - return FSDirectory.Open(path); - } + // Lucene is not thread safe on this call. + lock (_synLock) + { + return FSDirectory.Open(path); } } + } - private async Task WriteAsync(string indexName, Action action, bool close = false) + private async Task WriteAsync(string indexName, Action action, bool close = false) + { + if (!_writers.TryGetValue(indexName, out var writer)) { - if (!_writers.TryGetValue(indexName, out var writer)) + var indexAnalyzer = await _luceneIndexSettingsService.LoadIndexAnalyzerAsync(indexName); + lock (this) { - var indexAnalyzer = await _luceneIndexSettingsService.LoadIndexAnalyzerAsync(indexName); - lock (this) + if (!_writers.TryGetValue(indexName, out writer)) { - if (!_writers.TryGetValue(indexName, out writer)) + var directory = CreateDirectory(indexName); + var analyzer = _luceneAnalyzerManager.CreateAnalyzer(indexAnalyzer); + var config = new IndexWriterConfig(LuceneSettings.DefaultVersion, analyzer) { - var directory = CreateDirectory(indexName); - var analyzer = _luceneAnalyzerManager.CreateAnalyzer(indexAnalyzer); - var config = new IndexWriterConfig(LuceneSettings.DefaultVersion, analyzer) - { - OpenMode = OpenMode.CREATE_OR_APPEND, - WriteLockTimeout = LuceneLock.LOCK_POLL_INTERVAL * 3 - }; + OpenMode = OpenMode.CREATE_OR_APPEND, + WriteLockTimeout = LuceneLock.LOCK_POLL_INTERVAL * 3 + }; - writer = new IndexWriterWrapper(directory, config); + writer = new IndexWriterWrapper(directory, config); - if (close) - { - action?.Invoke(writer); - writer.Dispose(); - _timestamps[indexName] = _clock.UtcNow; - return; - } - - _writers[indexName] = writer; + if (close) + { + action?.Invoke(writer); + writer.Dispose(); + _timestamps[indexName] = _clock.UtcNow; + return; } + + _writers[indexName] = writer; } } + } - if (writer.IsClosing) - { - return; - } + if (writer.IsClosing) + { + return; + } - action?.Invoke(writer); + action?.Invoke(writer); - _timestamps[indexName] = _clock.UtcNow; - } + _timestamps[indexName] = _clock.UtcNow; + } - private IndexReaderPool.IndexReaderLease GetReader(string indexName) + private IndexReaderPool.IndexReaderLease GetReader(string indexName) + { + var pool = _indexPools.GetOrAdd(indexName, n => { - var pool = _indexPools.GetOrAdd(indexName, n => - { - var path = new DirectoryInfo(PathExtensions.Combine(_rootPath, indexName)); - var reader = DirectoryReader.Open(FSDirectory.Open(path)); - return new IndexReaderPool(reader); - }); + var path = new DirectoryInfo(PathExtensions.Combine(_rootPath, indexName)); + var reader = DirectoryReader.Open(FSDirectory.Open(path)); + return new IndexReaderPool(reader); + }); - return pool.Acquire(); - } + return pool.Acquire(); + } - /// - /// Releases all readers and writers. This can be used after some time of innactivity to free resources. - /// - public void FreeReaderWriter() + /// + /// Releases all readers and writers. This can be used after some time of innactivity to free resources. + /// + public void FreeReaderWriter() + { + lock (this) { - lock (this) + foreach (var writer in _writers) { - foreach (var writer in _writers) + if (_logger.IsEnabled(LogLevel.Information)) { - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("Freeing writer for: '{IndexName}'.", writer.Key); - } - - writer.Value.IsClosing = true; - writer.Value.Dispose(); + _logger.LogInformation("Freeing writer for: '{IndexName}'.", writer.Key); } - foreach (var reader in _indexPools) - { - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("Freeing reader for: '{IndexName}'.", reader.Key); - } + writer.Value.IsClosing = true; + writer.Value.Dispose(); + } - reader.Value.Dispose(); + foreach (var reader in _indexPools) + { + if (_logger.IsEnabled(LogLevel.Information)) + { + _logger.LogInformation("Freeing reader for: '{IndexName}'.", reader.Key); } - _writers.Clear(); - _indexPools.Clear(); - _timestamps.Clear(); + reader.Value.Dispose(); } + + _writers.Clear(); + _indexPools.Clear(); + _timestamps.Clear(); } + } - /// - /// Releases all readers and writers. This can be used after some time of innactivity to free resources. - /// - public void FreeReaderWriter(string indexName) + /// + /// Releases all readers and writers. This can be used after some time of innactivity to free resources. + /// + public void FreeReaderWriter(string indexName) + { + lock (this) { - lock (this) + if (_writers.TryGetValue(indexName, out var writer)) { - if (_writers.TryGetValue(indexName, out var writer)) + if (_logger.IsEnabled(LogLevel.Information)) { - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("Freeing writer for: '{IndexName}'.", indexName); - } - - writer.IsClosing = true; - writer.Dispose(); + _logger.LogInformation("Freeing writer for: '{IndexName}'.", indexName); } - if (_indexPools.TryGetValue(indexName, out var reader)) - { - if (_logger.IsEnabled(LogLevel.Information)) - { - _logger.LogInformation("Freeing reader for: '{IndexName}'.", indexName); - } - - reader.Dispose(); - } - - _timestamps.TryRemove(indexName, out _); - - _writers.TryRemove(indexName, out _); + writer.IsClosing = true; + writer.Dispose(); } - } - public void Dispose() - { - if (_disposed) + if (_indexPools.TryGetValue(indexName, out var reader)) { - return; - } + if (_logger.IsEnabled(LogLevel.Information)) + { + _logger.LogInformation("Freeing reader for: '{IndexName}'.", indexName); + } - _disposed = true; + reader.Dispose(); + } - FreeReaderWriter(); + _timestamps.TryRemove(indexName, out _); - GC.SuppressFinalize(this); + _writers.TryRemove(indexName, out _); } + } - ~LuceneIndexManager() + public void Dispose() + { + if (_disposed) { - if (_disposed) - { - return; - } - - FreeReaderWriter(); + return; } + + _disposed = true; + + FreeReaderWriter(); + + GC.SuppressFinalize(this); } - internal sealed class IndexWriterWrapper : IndexWriter + ~LuceneIndexManager() { - public IndexWriterWrapper(LDirectory directory, IndexWriterConfig config) : base(directory, config) + if (_disposed) { - IsClosing = false; + return; } - public bool IsClosing { get; set; } + FreeReaderWriter(); } +} - internal sealed class IndexReaderPool : IDisposable +internal sealed class IndexWriterWrapper : IndexWriter +{ + public IndexWriterWrapper(LDirectory directory, IndexWriterConfig config) : base(directory, config) { - private bool _dirty; - private int _count; - private readonly IndexReader _reader; + IsClosing = false; + } - public IndexReaderPool(IndexReader reader) - { - _reader = reader; - } + public bool IsClosing { get; set; } +} - public void MakeDirty() - { - _dirty = true; - } +internal sealed class IndexReaderPool : IDisposable +{ + private bool _dirty; + private int _count; + private readonly IndexReader _reader; - public IndexReaderLease Acquire() - { - return new IndexReaderLease(this, _reader); - } + public IndexReaderPool(IndexReader reader) + { + _reader = reader; + } - public void Release() - { - if (_dirty && _count == 0) - { - _reader.Dispose(); - } - } + public void MakeDirty() + { + _dirty = true; + } - public void Dispose() + public IndexReaderLease Acquire() + { + return new IndexReaderLease(this, _reader); + } + + public void Release() + { + if (_dirty && _count == 0) { _reader.Dispose(); } + } - public readonly struct IndexReaderLease : IDisposable - { - private readonly IndexReaderPool _pool; + public void Dispose() + { + _reader.Dispose(); + } - public IndexReaderLease(IndexReaderPool pool, IndexReader reader) - { - _pool = pool; - Interlocked.Increment(ref _pool._count); - IndexReader = reader; - } + public readonly struct IndexReaderLease : IDisposable + { + private readonly IndexReaderPool _pool; - public IndexReader IndexReader { get; } + public IndexReaderLease(IndexReaderPool pool, IndexReader reader) + { + _pool = pool; + Interlocked.Increment(ref _pool._count); + IndexReader = reader; + } - public void Dispose() - { - Interlocked.Decrement(ref _pool._count); - _pool.Release(); - } + public IndexReader IndexReader { get; } + + public void Dispose() + { + Interlocked.Decrement(ref _pool._count); + _pool.Release(); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexSettingsService.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexSettingsService.cs index b4a9cd4b1a0..339ffbc94a3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexSettingsService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexSettingsService.cs @@ -3,90 +3,89 @@ using OrchardCore.Documents; using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene -{ - public class LuceneIndexSettingsService - { - private readonly IDocumentManager _documentManager; - - public LuceneIndexSettingsService(IDocumentManager documentManager) - { - _documentManager = documentManager; - } +namespace OrchardCore.Search.Lucene; - /// - /// Loads the index settings document from the store for updating and that should not be cached. - /// - public Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); +public class LuceneIndexSettingsService +{ + private readonly IDocumentManager _documentManager; - /// - /// Gets the index settings document from the cache for sharing and that should not be updated. - /// - public async Task GetDocumentAsync() - { - var document = await _documentManager.GetOrCreateImmutableAsync(); + public LuceneIndexSettingsService(IDocumentManager documentManager) + { + _documentManager = documentManager; + } - foreach (var name in document.LuceneIndexSettings.Keys) - { - document.LuceneIndexSettings[name].IndexName = name; - } + /// + /// Loads the index settings document from the store for updating and that should not be cached. + /// + public Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); - return document; - } + /// + /// Gets the index settings document from the cache for sharing and that should not be updated. + /// + public async Task GetDocumentAsync() + { + var document = await _documentManager.GetOrCreateImmutableAsync(); - public async Task> GetSettingsAsync() + foreach (var name in document.LuceneIndexSettings.Keys) { - return (await GetDocumentAsync()).LuceneIndexSettings.Values; + document.LuceneIndexSettings[name].IndexName = name; } - public async Task GetSettingsAsync(string indexName) - { - var document = await GetDocumentAsync(); + return document; + } - if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) - { - return settings; - } + public async Task> GetSettingsAsync() + { + return (await GetDocumentAsync()).LuceneIndexSettings.Values; + } - return null; - } + public async Task GetSettingsAsync(string indexName) + { + var document = await GetDocumentAsync(); - public async Task GetIndexAnalyzerAsync(string indexName) + if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) { - var document = await GetDocumentAsync(); + return settings; + } - if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) - { - return settings.AnalyzerName; - } + return null; + } - return LuceneSettings.StandardAnalyzer; - } + public async Task GetIndexAnalyzerAsync(string indexName) + { + var document = await GetDocumentAsync(); - public async Task LoadIndexAnalyzerAsync(string indexName) + if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) { - var document = await LoadDocumentAsync(); + return settings.AnalyzerName; + } - if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) - { - return settings.AnalyzerName; - } + return LuceneSettings.StandardAnalyzer; + } - return LuceneSettings.StandardAnalyzer; - } + public async Task LoadIndexAnalyzerAsync(string indexName) + { + var document = await LoadDocumentAsync(); - public async Task UpdateIndexAsync(LuceneIndexSettings settings) + if (document.LuceneIndexSettings.TryGetValue(indexName, out var settings)) { - var document = await LoadDocumentAsync(); - document.LuceneIndexSettings[settings.IndexName] = settings; - await _documentManager.UpdateAsync(document); + return settings.AnalyzerName; } - public async Task DeleteIndexAsync(string indexName) - { - var document = await LoadDocumentAsync(); - document.LuceneIndexSettings.Remove(indexName); - await _documentManager.UpdateAsync(document); - } + return LuceneSettings.StandardAnalyzer; + } + + public async Task UpdateIndexAsync(LuceneIndexSettings settings) + { + var document = await LoadDocumentAsync(); + document.LuceneIndexSettings[settings.IndexName] = settings; + await _documentManager.UpdateAsync(document); + } + + public async Task DeleteIndexAsync(string indexName) + { + var document = await LoadDocumentAsync(); + document.LuceneIndexSettings.Remove(indexName); + await _documentManager.UpdateAsync(document); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingService.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingService.cs index c633e130e8d..d216b0c4a9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingService.cs @@ -12,291 +12,290 @@ using OrchardCore.Search.Lucene.Model; using OrchardCore.Settings; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +/// +/// This class provides services to update all the Lucene indices. It is non-rentrant so that calls +/// from different components can be done simultaneously, e.g. from a background task, an event or a UI interaction. +/// It also indexes one content item at a time and provides the result to all indices. +/// +public class LuceneIndexingService { - /// - /// This class provides services to update all the Lucene indices. It is non-rentrant so that calls - /// from different components can be done simultaneously, e.g. from a background task, an event or a UI interaction. - /// It also indexes one content item at a time and provides the result to all indices. - /// - public class LuceneIndexingService + private const int BatchSize = 100; + private readonly IShellHost _shellHost; + private readonly ShellSettings _shellSettings; + private readonly LuceneIndexingState _indexingState; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly LuceneIndexManager _indexManager; + private readonly IIndexingTaskManager _indexingTaskManager; + private readonly ISiteService _siteService; + private readonly ILogger _logger; + + public LuceneIndexingService( + IShellHost shellHost, + ShellSettings shellSettings, + LuceneIndexingState indexingState, + LuceneIndexSettingsService luceneIndexSettingsService, + LuceneIndexManager indexManager, + IIndexingTaskManager indexingTaskManager, + ISiteService siteService, + ILogger logger) { - private const int BatchSize = 100; - private readonly IShellHost _shellHost; - private readonly ShellSettings _shellSettings; - private readonly LuceneIndexingState _indexingState; - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - private readonly LuceneIndexManager _indexManager; - private readonly IIndexingTaskManager _indexingTaskManager; - private readonly ISiteService _siteService; - private readonly ILogger _logger; - - public LuceneIndexingService( - IShellHost shellHost, - ShellSettings shellSettings, - LuceneIndexingState indexingState, - LuceneIndexSettingsService luceneIndexSettingsService, - LuceneIndexManager indexManager, - IIndexingTaskManager indexingTaskManager, - ISiteService siteService, - ILogger logger) - { - _shellHost = shellHost; - _shellSettings = shellSettings; - _indexingState = indexingState; - _luceneIndexSettingsService = luceneIndexSettingsService; - _indexManager = indexManager; - _indexingTaskManager = indexingTaskManager; - _siteService = siteService; - _logger = logger; - } + _shellHost = shellHost; + _shellSettings = shellSettings; + _indexingState = indexingState; + _luceneIndexSettingsService = luceneIndexSettingsService; + _indexManager = indexManager; + _indexingTaskManager = indexingTaskManager; + _siteService = siteService; + _logger = logger; + } + + public async Task ProcessContentItemsAsync(string indexName = default) + { + // TODO: Lock over the filesystem in case two instances get a command to rebuild the index concurrently. + var allIndices = new Dictionary(); + var lastTaskId = long.MaxValue; + IEnumerable indexSettingsList = null; - public async Task ProcessContentItemsAsync(string indexName = default) + if (string.IsNullOrEmpty(indexName)) { - // TODO: Lock over the filesystem in case two instances get a command to rebuild the index concurrently. - var allIndices = new Dictionary(); - var lastTaskId = long.MaxValue; - IEnumerable indexSettingsList = null; + indexSettingsList = await _luceneIndexSettingsService.GetSettingsAsync(); - if (string.IsNullOrEmpty(indexName)) + if (!indexSettingsList.Any()) { - indexSettingsList = await _luceneIndexSettingsService.GetSettingsAsync(); - - if (!indexSettingsList.Any()) - { - return; - } - - // Find the lowest task id to process. - foreach (var indexSetting in indexSettingsList) - { - var taskId = _indexingState.GetLastTaskId(indexSetting.IndexName); - lastTaskId = Math.Min(lastTaskId, taskId); - allIndices.Add(indexSetting.IndexName, taskId); - } + return; } - else - { - var settings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); - - if (settings == null) - { - return; - } - - indexSettingsList = new LuceneIndexSettings[1] { settings }.AsEnumerable(); - var taskId = _indexingState.GetLastTaskId(indexName); + // Find the lowest task id to process. + foreach (var indexSetting in indexSettingsList) + { + var taskId = _indexingState.GetLastTaskId(indexSetting.IndexName); lastTaskId = Math.Min(lastTaskId, taskId); - allIndices.Add(indexName, taskId); + allIndices.Add(indexSetting.IndexName, taskId); } + } + else + { + var settings = await _luceneIndexSettingsService.GetSettingsAsync(indexName); - if (allIndices.Count == 0) + if (settings == null) { return; } - var batch = Array.Empty(); + indexSettingsList = new LuceneIndexSettings[1] { settings }.AsEnumerable(); + + var taskId = _indexingState.GetLastTaskId(indexName); + lastTaskId = Math.Min(lastTaskId, taskId); + allIndices.Add(indexName, taskId); + } + + if (allIndices.Count == 0) + { + return; + } - do + var batch = Array.Empty(); + + do + { + // Create a scope for the content manager. + var shellScope = await _shellHost.GetScopeAsync(_shellSettings); + + await shellScope.UsingAsync(async scope => { - // Create a scope for the content manager. - var shellScope = await _shellHost.GetScopeAsync(_shellSettings); + // Load the next batch of tasks. + batch = (await _indexingTaskManager.GetIndexingTasksAsync(lastTaskId, BatchSize)).ToArray(); - await shellScope.UsingAsync(async scope => + if (batch.Length == 0) { - // Load the next batch of tasks. - batch = (await _indexingTaskManager.GetIndexingTasksAsync(lastTaskId, BatchSize)).ToArray(); - - if (batch.Length == 0) - { - return; - } + return; + } - var contentManager = scope.ServiceProvider.GetRequiredService(); - var indexHandlers = scope.ServiceProvider.GetServices(); + var contentManager = scope.ServiceProvider.GetRequiredService(); + var indexHandlers = scope.ServiceProvider.GetServices(); - // Pre-load all content items to prevent SELECT N+1. - var updatedContentItemIds = batch - .Where(x => x.Type == IndexingTaskTypes.Update) - .Select(x => x.ContentItemId) - .ToArray(); + // Pre-load all content items to prevent SELECT N+1. + var updatedContentItemIds = batch + .Where(x => x.Type == IndexingTaskTypes.Update) + .Select(x => x.ContentItemId) + .ToArray(); - var allPublished = new Dictionary(); - var allLatest = new Dictionary(); + var allPublished = new Dictionary(); + var allLatest = new Dictionary(); - var allPublishedContentItems = await contentManager.GetAsync(updatedContentItemIds); - allPublished = allPublishedContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v); - var allLatestContentItems = await contentManager.GetAsync(updatedContentItemIds, VersionOptions.Latest); - allLatest = allLatestContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v); + var allPublishedContentItems = await contentManager.GetAsync(updatedContentItemIds); + allPublished = allPublishedContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v); + var allLatestContentItems = await contentManager.GetAsync(updatedContentItemIds, VersionOptions.Latest); + allLatest = allLatestContentItems.DistinctBy(x => x.ContentItemId).ToDictionary(k => k.ContentItemId, v => v); - // Group all DocumentIndex by index to batch update them. - var updatedDocumentsByIndex = new Dictionary>(); + // Group all DocumentIndex by index to batch update them. + var updatedDocumentsByIndex = new Dictionary>(); - foreach (var index in allIndices) - { - updatedDocumentsByIndex[index.Key] = []; - } + foreach (var index in allIndices) + { + updatedDocumentsByIndex[index.Key] = []; + } - if (indexName != null) - { - indexSettingsList = indexSettingsList.Where(x => x.IndexName == indexName); - } + if (indexName != null) + { + indexSettingsList = indexSettingsList.Where(x => x.IndexName == indexName); + } - var needLatest = indexSettingsList.FirstOrDefault(x => x.IndexLatest) != null; - var needPublished = indexSettingsList.FirstOrDefault(x => !x.IndexLatest) != null; + var needLatest = indexSettingsList.FirstOrDefault(x => x.IndexLatest) != null; + var needPublished = indexSettingsList.FirstOrDefault(x => !x.IndexLatest) != null; - var settingsByIndex = indexSettingsList.ToDictionary(x => x.IndexName, x => x); + var settingsByIndex = indexSettingsList.ToDictionary(x => x.IndexName, x => x); - foreach (var task in batch) + foreach (var task in batch) + { + if (task.Type == IndexingTaskTypes.Update) { - if (task.Type == IndexingTaskTypes.Update) + BuildIndexContext publishedIndexContext = null, latestIndexContext = null; + + if (needPublished) { - BuildIndexContext publishedIndexContext = null, latestIndexContext = null; + allPublished.TryGetValue(task.ContentItemId, out var contentItem); - if (needPublished) + if (contentItem != null) { - allPublished.TryGetValue(task.ContentItemId, out var contentItem); + publishedIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); + await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(publishedIndexContext), _logger); + } + } + + if (needLatest) + { + allLatest.TryGetValue(task.ContentItemId, out var contentItem); - if (contentItem != null) - { - publishedIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); - await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(publishedIndexContext), _logger); - } + if (contentItem != null) + { + latestIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); + await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(latestIndexContext), _logger); } + } - if (needLatest) + // Update the document from the index if its lastIndexId is smaller than the current task id. + foreach (var index in allIndices) + { + if (index.Value >= task.Id || !settingsByIndex.TryGetValue(index.Key, out var settings)) { - allLatest.TryGetValue(task.ContentItemId, out var contentItem); + continue; + } + + var context = !settings.IndexLatest ? publishedIndexContext : latestIndexContext; - if (contentItem != null) - { - latestIndexContext = new BuildIndexContext(new DocumentIndex(task.ContentItemId, contentItem.ContentItemVersionId), contentItem, new string[] { contentItem.ContentType }, new LuceneContentIndexSettings()); - await indexHandlers.InvokeAsync(x => x.BuildIndexAsync(latestIndexContext), _logger); - } + // We index only if we actually found a content item in the database. + if (context == null) + { + // TODO purge these content items from IndexingTask table. + continue; } - // Update the document from the index if its lastIndexId is smaller than the current task id. - foreach (var index in allIndices) + var cultureAspect = await contentManager.PopulateAspectAsync(context.ContentItem); + var culture = cultureAspect.HasCulture ? cultureAspect.Culture.Name : null; + var ignoreIndexedCulture = settings.Culture != "any" && culture != settings.Culture; + + // Ignore if the content item content type or culture is not indexed in this index. + if (!settings.IndexedContentTypes.Contains(context.ContentItem.ContentType) || ignoreIndexedCulture) { - if (index.Value >= task.Id || !settingsByIndex.TryGetValue(index.Key, out var settings)) - { - continue; - } - - var context = !settings.IndexLatest ? publishedIndexContext : latestIndexContext; - - // We index only if we actually found a content item in the database. - if (context == null) - { - // TODO purge these content items from IndexingTask table. - continue; - } - - var cultureAspect = await contentManager.PopulateAspectAsync(context.ContentItem); - var culture = cultureAspect.HasCulture ? cultureAspect.Culture.Name : null; - var ignoreIndexedCulture = settings.Culture != "any" && culture != settings.Culture; - - // Ignore if the content item content type or culture is not indexed in this index. - if (!settings.IndexedContentTypes.Contains(context.ContentItem.ContentType) || ignoreIndexedCulture) - { - continue; - } - - updatedDocumentsByIndex[index.Key].Add(context.DocumentIndex); + continue; } + + updatedDocumentsByIndex[index.Key].Add(context.DocumentIndex); } } + } - // Delete all the existing documents. - foreach (var index in updatedDocumentsByIndex) - { - var deletedDocuments = updatedDocumentsByIndex[index.Key].Select(x => x.ContentItemId); + // Delete all the existing documents. + foreach (var index in updatedDocumentsByIndex) + { + var deletedDocuments = updatedDocumentsByIndex[index.Key].Select(x => x.ContentItemId); - await _indexManager.DeleteDocumentsAsync(index.Key, deletedDocuments); - } + await _indexManager.DeleteDocumentsAsync(index.Key, deletedDocuments); + } - // Submits all the new documents to the index. - foreach (var index in updatedDocumentsByIndex) - { - await _indexManager.StoreDocumentsAsync(index.Key, updatedDocumentsByIndex[index.Key]); - } + // Submits all the new documents to the index. + foreach (var index in updatedDocumentsByIndex) + { + await _indexManager.StoreDocumentsAsync(index.Key, updatedDocumentsByIndex[index.Key]); + } - // Update task ids. - lastTaskId = batch.Last().Id; + // Update task ids. + lastTaskId = batch.Last().Id; - foreach (var indexStatus in allIndices) + foreach (var indexStatus in allIndices) + { + if (indexStatus.Value < lastTaskId) { - if (indexStatus.Value < lastTaskId) - { - _indexingState.SetLastTaskId(indexStatus.Key, lastTaskId); - } + _indexingState.SetLastTaskId(indexStatus.Key, lastTaskId); } + } - _indexingState.Update(); - }, activateShell: false); - } while (batch.Length == BatchSize); - } + _indexingState.Update(); + }, activateShell: false); + } while (batch.Length == BatchSize); + } - /// - /// Creates a new index. - /// - /// - public async Task CreateIndexAsync(LuceneIndexSettings indexSettings) - { - await _luceneIndexSettingsService.UpdateIndexAsync(indexSettings); - await RebuildIndexAsync(indexSettings.IndexName); - } + /// + /// Creates a new index. + /// + /// + public async Task CreateIndexAsync(LuceneIndexSettings indexSettings) + { + await _luceneIndexSettingsService.UpdateIndexAsync(indexSettings); + await RebuildIndexAsync(indexSettings.IndexName); + } - /// - /// Update an existing index. - /// - /// - public Task UpdateIndexAsync(LuceneIndexSettings indexSettings) + /// + /// Update an existing index. + /// + /// + public Task UpdateIndexAsync(LuceneIndexSettings indexSettings) + { + return _luceneIndexSettingsService.UpdateIndexAsync(indexSettings); + } + + /// + /// Deletes permanently an index. + /// + /// + public Task DeleteIndexAsync(string indexName) + { + if (_indexManager.Exists(indexName)) { - return _luceneIndexSettingsService.UpdateIndexAsync(indexSettings); + _indexManager.DeleteIndex(indexName); } - /// - /// Deletes permanently an index. - /// - /// - public Task DeleteIndexAsync(string indexName) - { - if (_indexManager.Exists(indexName)) - { - _indexManager.DeleteIndex(indexName); - } + return _luceneIndexSettingsService.DeleteIndexAsync(indexName); + } - return _luceneIndexSettingsService.DeleteIndexAsync(indexName); - } + /// + /// Restarts the indexing process from the beginning in order to update + /// current content items. It doesn't delete existing entries from the index. + /// + public void ResetIndexAsync(string indexName) + { + _indexingState.SetLastTaskId(indexName, 0); + _indexingState.Update(); + } - /// - /// Restarts the indexing process from the beginning in order to update - /// current content items. It doesn't delete existing entries from the index. - /// - public void ResetIndexAsync(string indexName) + /// + /// Deletes and recreates the full index content. + /// + public async Task RebuildIndexAsync(string indexName) + { + if (_indexManager.Exists(indexName)) { - _indexingState.SetLastTaskId(indexName, 0); - _indexingState.Update(); + _indexManager.DeleteIndex(indexName); } - /// - /// Deletes and recreates the full index content. - /// - public async Task RebuildIndexAsync(string indexName) - { - if (_indexManager.Exists(indexName)) - { - _indexManager.DeleteIndex(indexName); - } - - await _indexManager.CreateIndexAsync(indexName); + await _indexManager.CreateIndexAsync(indexName); - ResetIndexAsync(indexName); - } - - public async Task GetLuceneSettingsAsync() - => await _siteService.GetSettingsAsync() ?? new LuceneSettings(); + ResetIndexAsync(indexName); } + + public async Task GetLuceneSettingsAsync() + => await _siteService.GetSettingsAsync() ?? new LuceneSettings(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingState.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingState.cs index 6a1bdc67cb2..f4a44349da6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingState.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneIndexingState.cs @@ -4,69 +4,68 @@ using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +/// +/// This class persists the indexing state, a cursor, on the filesystem alongside the index itself. +/// This state has to be on the filesystem as each node has its own local storage for the index. +/// +public class LuceneIndexingState { - /// - /// This class persists the indexing state, a cursor, on the filesystem alongside the index itself. - /// This state has to be on the filesystem as each node has its own local storage for the index. - /// - public class LuceneIndexingState + private readonly string _indexSettingsFilename; + private readonly JsonObject _content; + + public LuceneIndexingState( + IOptions shellOptions, + ShellSettings shellSettings + ) { - private readonly string _indexSettingsFilename; - private readonly JsonObject _content; + _indexSettingsFilename = PathExtensions.Combine( + shellOptions.Value.ShellsApplicationDataPath, + shellOptions.Value.ShellsContainerName, + shellSettings.Name, + "lucene.status.json"); - public LuceneIndexingState( - IOptions shellOptions, - ShellSettings shellSettings - ) + if (!File.Exists(_indexSettingsFilename)) { - _indexSettingsFilename = PathExtensions.Combine( - shellOptions.Value.ShellsApplicationDataPath, - shellOptions.Value.ShellsContainerName, - shellSettings.Name, - "lucene.status.json"); + Directory.CreateDirectory(Path.GetDirectoryName(_indexSettingsFilename)); - if (!File.Exists(_indexSettingsFilename)) - { - Directory.CreateDirectory(Path.GetDirectoryName(_indexSettingsFilename)); + File.WriteAllText(_indexSettingsFilename, new JsonObject().ToJsonString(JOptions.Indented)); + } - File.WriteAllText(_indexSettingsFilename, new JsonObject().ToJsonString(JOptions.Indented)); - } + _content = JObject.Parse(File.ReadAllText(_indexSettingsFilename)); + } - _content = JObject.Parse(File.ReadAllText(_indexSettingsFilename)); + public long GetLastTaskId(string indexName) + { + if (_content.TryGetPropertyValue(indexName, out var value)) + { + return value.Value(); } - - public long GetLastTaskId(string indexName) + else { - if (_content.TryGetPropertyValue(indexName, out var value)) + lock (this) { - return value.Value(); + _content.Add(indexName, JsonValue.Create(0)); } - else - { - lock (this) - { - _content.Add(indexName, JsonValue.Create(0)); - } - return 0L; - } + return 0L; } + } - public void SetLastTaskId(string indexName, long taskId) + public void SetLastTaskId(string indexName, long taskId) + { + lock (this) { - lock (this) - { - _content[indexName] = taskId; - } + _content[indexName] = taskId; } + } - public void Update() + public void Update() + { + lock (this) { - lock (this) - { - File.WriteAllText(_indexSettingsFilename, _content.ToJsonString(JOptions.Indented)); - } + File.WriteAllText(_indexSettingsFilename, _content.ToJsonString(JOptions.Indented)); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneQuerySource.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneQuerySource.cs index f68d9156e14..d4d05b4b44c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneQuerySource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneQuerySource.cs @@ -16,92 +16,91 @@ using YesSql; using YesSql.Services; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +public sealed class LuceneQuerySource : IQuerySource { - public sealed class LuceneQuerySource : IQuerySource + public const string SourceName = "Lucene"; + + private readonly LuceneIndexManager _luceneIndexManager; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly LuceneAnalyzerManager _luceneAnalyzerManager; + private readonly ILuceneQueryService _queryService; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly ISession _session; + private readonly JavaScriptEncoder _javaScriptEncoder; + private readonly TemplateOptions _templateOptions; + + public LuceneQuerySource( + LuceneIndexManager luceneIndexManager, + LuceneIndexSettingsService luceneIndexSettingsService, + LuceneAnalyzerManager luceneAnalyzerManager, + ILuceneQueryService queryService, + ILiquidTemplateManager liquidTemplateManager, + ISession session, + JavaScriptEncoder javaScriptEncoder, + IOptions templateOptions) { - public const string SourceName = "Lucene"; + _luceneIndexManager = luceneIndexManager; + _luceneIndexSettingsService = luceneIndexSettingsService; + _luceneAnalyzerManager = luceneAnalyzerManager; + _queryService = queryService; + _liquidTemplateManager = liquidTemplateManager; + _session = session; + _javaScriptEncoder = javaScriptEncoder; + _templateOptions = templateOptions.Value; + } - private readonly LuceneIndexManager _luceneIndexManager; - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - private readonly LuceneAnalyzerManager _luceneAnalyzerManager; - private readonly ILuceneQueryService _queryService; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly ISession _session; - private readonly JavaScriptEncoder _javaScriptEncoder; - private readonly TemplateOptions _templateOptions; + public string Name + => SourceName; - public LuceneQuerySource( - LuceneIndexManager luceneIndexManager, - LuceneIndexSettingsService luceneIndexSettingsService, - LuceneAnalyzerManager luceneAnalyzerManager, - ILuceneQueryService queryService, - ILiquidTemplateManager liquidTemplateManager, - ISession session, - JavaScriptEncoder javaScriptEncoder, - IOptions templateOptions) + public async Task ExecuteQueryAsync(Query query, IDictionary parameters) + { + var luceneQueryResults = new LuceneQueryResults(); + var metadata = query.As(); + + await _luceneIndexManager.SearchAsync(metadata.Index, async searcher => { - _luceneIndexManager = luceneIndexManager; - _luceneIndexSettingsService = luceneIndexSettingsService; - _luceneAnalyzerManager = luceneAnalyzerManager; - _queryService = queryService; - _liquidTemplateManager = liquidTemplateManager; - _session = session; - _javaScriptEncoder = javaScriptEncoder; - _templateOptions = templateOptions.Value; - } + var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(metadata.Template, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); - public string Name - => SourceName; + var parameterizedQuery = JsonNode.Parse(tokenizedContent).AsObject(); - public async Task ExecuteQueryAsync(Query query, IDictionary parameters) - { - var luceneQueryResults = new LuceneQueryResults(); - var metadata = query.As(); + var analyzer = _luceneAnalyzerManager.CreateAnalyzer(await _luceneIndexSettingsService.GetIndexAnalyzerAsync(metadata.Index)); + var context = new LuceneQueryContext(searcher, LuceneSettings.DefaultVersion, analyzer); + var docs = await _queryService.SearchAsync(context, parameterizedQuery); + luceneQueryResults.Count = docs.Count; - await _luceneIndexManager.SearchAsync(metadata.Index, async searcher => + if (query.ReturnContentItems) { - var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(metadata.Template, _javaScriptEncoder, parameters.Select(x => new KeyValuePair(x.Key, FluidValue.Create(x.Value, _templateOptions)))); - - var parameterizedQuery = JsonNode.Parse(tokenizedContent).AsObject(); + // We always return an empty collection if the bottom lines queries have no results. + luceneQueryResults.Items = []; - var analyzer = _luceneAnalyzerManager.CreateAnalyzer(await _luceneIndexSettingsService.GetIndexAnalyzerAsync(metadata.Index)); - var context = new LuceneQueryContext(searcher, LuceneSettings.DefaultVersion, analyzer); - var docs = await _queryService.SearchAsync(context, parameterizedQuery); - luceneQueryResults.Count = docs.Count; + // Load corresponding content item versions. + var indexedContentItemVersionIds = docs.TopDocs.ScoreDocs.Select(x => searcher.Doc(x.Doc).Get("ContentItemVersionId")).ToArray(); + var dbContentItems = await _session.Query(x => x.ContentItemVersionId.IsIn(indexedContentItemVersionIds)).ListAsync(); - if (query.ReturnContentItems) + // Reorder the result to preserve the one from the lucene query. + if (dbContentItems.Any()) { - // We always return an empty collection if the bottom lines queries have no results. - luceneQueryResults.Items = []; - - // Load corresponding content item versions. - var indexedContentItemVersionIds = docs.TopDocs.ScoreDocs.Select(x => searcher.Doc(x.Doc).Get("ContentItemVersionId")).ToArray(); - var dbContentItems = await _session.Query(x => x.ContentItemVersionId.IsIn(indexedContentItemVersionIds)).ListAsync(); - - // Reorder the result to preserve the one from the lucene query. - if (dbContentItems.Any()) - { - var dbContentItemVersionIds = dbContentItems.ToDictionary(x => x.ContentItemVersionId, x => x); - var indexedAndInDB = indexedContentItemVersionIds.Where(dbContentItemVersionIds.ContainsKey); - luceneQueryResults.Items = indexedAndInDB.Select(x => dbContentItemVersionIds[x]).ToArray(); - } + var dbContentItemVersionIds = dbContentItems.ToDictionary(x => x.ContentItemVersionId, x => x); + var indexedAndInDB = indexedContentItemVersionIds.Where(dbContentItemVersionIds.ContainsKey); + luceneQueryResults.Items = indexedAndInDB.Select(x => dbContentItemVersionIds[x]).ToArray(); } - else - { - var results = new List(); - - foreach (var document in docs.TopDocs.ScoreDocs.Select(hit => searcher.Doc(hit.Doc))) - { - results.Add(new JsonObject(document.Select(x => - KeyValuePair.Create(x.Name, (JsonNode)JsonValue.Create(x.GetStringValue()))))); - } + } + else + { + var results = new List(); - luceneQueryResults.Items = results; + foreach (var document in docs.TopDocs.ScoreDocs.Select(hit => searcher.Doc(hit.Doc))) + { + results.Add(new JsonObject(document.Select(x => + KeyValuePair.Create(x.Name, (JsonNode)JsonValue.Create(x.GetStringValue()))))); } - }); - return luceneQueryResults; - } + luceneQueryResults.Items = results; + } + }); + + return luceneQueryResults; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneSearchQueryService.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneSearchQueryService.cs index a3958366b06..0274ccfd78f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneSearchQueryService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Services/LuceneSearchQueryService.cs @@ -2,43 +2,42 @@ using System.Threading.Tasks; using Lucene.Net.Search; -namespace OrchardCore.Search.Lucene.Services +namespace OrchardCore.Search.Lucene.Services; + +public class LuceneSearchQueryService : ILuceneSearchQueryService { - public class LuceneSearchQueryService : ILuceneSearchQueryService - { - private readonly LuceneIndexManager _luceneIndexManager; + private readonly LuceneIndexManager _luceneIndexManager; - private static readonly HashSet _idSet = new(["ContentItemId"]); + private static readonly HashSet _idSet = new(["ContentItemId"]); - public LuceneSearchQueryService(LuceneIndexManager luceneIndexManager) - { - _luceneIndexManager = luceneIndexManager; - } + public LuceneSearchQueryService(LuceneIndexManager luceneIndexManager) + { + _luceneIndexManager = luceneIndexManager; + } - public async Task> ExecuteQueryAsync(Query query, string indexName, int start, int end) - { - var contentItemIds = new List(); + public async Task> ExecuteQueryAsync(Query query, string indexName, int start, int end) + { + var contentItemIds = new List(); - await _luceneIndexManager.SearchAsync(indexName, searcher => + await _luceneIndexManager.SearchAsync(indexName, searcher => + { + if (end > 0) { - if (end > 0) - { - var collector = TopScoreDocCollector.Create(end, true); + var collector = TopScoreDocCollector.Create(end, true); - searcher.Search(query, collector); - var hits = collector.GetTopDocs(start, end); + searcher.Search(query, collector); + var hits = collector.GetTopDocs(start, end); - foreach (var hit in hits.ScoreDocs) - { - var d = searcher.Doc(hit.Doc, _idSet); - contentItemIds.Add(d.GetField("ContentItemId").GetStringValue()); - } + foreach (var hit in hits.ScoreDocs) + { + var d = searcher.Doc(hit.Doc, _idSet); + contentItemIds.Add(d.GetField("ContentItemId").GetStringValue()); } + } - return Task.CompletedTask; - }); + return Task.CompletedTask; + }); - return contentItemIds; - } + return contentItemIds; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPartFieldIndexSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPartFieldIndexSettingsDisplayDriver.cs index cbcc28fd7f5..e29c3a159d6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPartFieldIndexSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPartFieldIndexSettingsDisplayDriver.cs @@ -8,48 +8,47 @@ using OrchardCore.Search.Lucene.Model; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Settings +namespace OrchardCore.Search.Lucene.Settings; + +public sealed class ContentPartFieldIndexSettingsDisplayDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class ContentPartFieldIndexSettingsDisplayDriver : ContentPartFieldDefinitionDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentPartFieldIndexSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public ContentPartFieldIndexSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext updater) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - public override async Task EditAsync(ContentPartFieldDefinition contentPartFieldDefinition, BuildEditorContext updater) + return Initialize("LuceneContentIndexSettings_Edit", model => { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) - { - return null; - } - - return Initialize("LuceneContentIndexSettings_Edit", model => - { - model.LuceneContentIndexSettings = contentPartFieldDefinition.GetSettings(); - }).Location("Content:10"); - } + model.LuceneContentIndexSettings = contentPartFieldDefinition.GetSettings(); + }).Location("Content:10"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition contentPartFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition contentPartFieldDefinition, UpdatePartFieldEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) - { - return null; - } + return null; + } - var model = new LuceneContentIndexSettingsViewModel(); + var model = new LuceneContentIndexSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model.LuceneContentIndexSettings); + context.Builder.WithSettings(model.LuceneContentIndexSettings); - return await EditAsync(contentPartFieldDefinition, context); - } + return await EditAsync(contentPartFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettings.cs index 447df5ebb81..ee7d86fc2d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettings.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Search.Lucene.Settings +namespace OrchardCore.Search.Lucene.Settings; + +public class ContentPickerFieldLuceneEditorSettings { - public class ContentPickerFieldLuceneEditorSettings - { - public string Index { get; set; } + public string Index { get; set; } - [BindNever] - public string[] Indices { get; set; } - } + [BindNever] + public string[] Indices { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettingsDriver.cs index b56d1c2be31..26e6a2cffc2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentPickerFieldLuceneEditorSettingsDriver.cs @@ -7,45 +7,44 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Search.Lucene.Settings +namespace OrchardCore.Search.Lucene.Settings; + +public sealed class ContentPickerFieldLuceneEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class ContentPickerFieldLuceneEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver - { - private readonly LuceneIndexSettingsService _luceneIndexSettingsService; + private readonly LuceneIndexSettingsService _luceneIndexSettingsService; - public ContentPickerFieldLuceneEditorSettingsDriver(LuceneIndexSettingsService luceneIndexSettingsService) - { - _luceneIndexSettingsService = luceneIndexSettingsService; - } + public ContentPickerFieldLuceneEditorSettingsDriver(LuceneIndexSettingsService luceneIndexSettingsService) + { + _luceneIndexSettingsService = luceneIndexSettingsService; + } - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + { + return Initialize("ContentPickerFieldLuceneEditorSettings_Edit", async model => { - return Initialize("ContentPickerFieldLuceneEditorSettings_Edit", async model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Index = settings.Index; - model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); - }).Location("Editor"); - } + model.Index = settings.Index; + model.Indices = (await _luceneIndexSettingsService.GetSettingsAsync()).Select(x => x.IndexName).ToArray(); + }).Location("Editor"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Lucene") { - if (partFieldDefinition.Editor() == "Lucene") - { - var model = new ContentPickerFieldLuceneEditorSettings(); + var model = new ContentPickerFieldLuceneEditorSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); - } - - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(model); } - public override bool CanHandleModel(ContentPartFieldDefinition model) - { - return string.Equals("ContentPickerField", model.FieldDefinition.Name, StringComparison.Ordinal); - } + return Edit(partFieldDefinition, context); + } + + public override bool CanHandleModel(ContentPartFieldDefinition model) + { + return string.Equals("ContentPickerField", model.FieldDefinition.Name, StringComparison.Ordinal); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentTypePartIndexSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentTypePartIndexSettingsDisplayDriver.cs index 5b0e64c8ca0..7fb67654c78 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentTypePartIndexSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Settings/ContentTypePartIndexSettingsDisplayDriver.cs @@ -8,48 +8,47 @@ using OrchardCore.Search.Lucene.Model; using OrchardCore.Search.Lucene.ViewModels; -namespace OrchardCore.Search.Lucene.Settings +namespace OrchardCore.Search.Lucene.Settings; + +public sealed class ContentTypePartIndexSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class ContentTypePartIndexSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + + public ContentTypePartIndexSettingsDisplayDriver( + IAuthorizationService authorizationService, + IHttpContextAccessor httpContextAccessor) { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + } - public ContentTypePartIndexSettingsDisplayDriver( - IAuthorizationService authorizationService, - IHttpContextAccessor httpContextAccessor) + public override async Task EditAsync(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; + return null; } - public override async Task EditAsync(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("LuceneContentIndexSettings_Edit", model => { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) - { - return null; - } - - return Initialize("LuceneContentIndexSettings_Edit", model => - { - model.LuceneContentIndexSettings = contentTypePartDefinition.GetSettings(); - }).Location("Content:10"); - } + model.LuceneContentIndexSettings = contentTypePartDefinition.GetSettings(); + }).Location("Content:10"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) { - if (!await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User, Permissions.ManageLuceneIndexes)) - { - return null; - } + return null; + } - var model = new LuceneContentIndexSettingsViewModel(); + var model = new LuceneContentIndexSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model.LuceneContentIndexSettings); + context.Builder.WithSettings(model.LuceneContentIndexSettings); - return await EditAsync(contentTypePartDefinition, context); - } + return await EditAsync(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Shapes/LuceneContentPickerShapeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Shapes/LuceneContentPickerShapeProvider.cs index 7c2335d0278..246bc0cbe65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Shapes/LuceneContentPickerShapeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Shapes/LuceneContentPickerShapeProvider.cs @@ -4,28 +4,27 @@ using OrchardCore.DisplayManagement.Descriptors; using OrchardCore.DisplayManagement.Shapes; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +public class LuceneContentPickerShapeProvider : IShapeAttributeProvider { - public class LuceneContentPickerShapeProvider : IShapeAttributeProvider - { - protected readonly IStringLocalizer S; + protected readonly IStringLocalizer S; - public LuceneContentPickerShapeProvider(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public LuceneContentPickerShapeProvider(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } - [Shape] + [Shape] #pragma warning disable CA1707 // Remove the underscores from member name - public IHtmlContent ContentPickerField_Option__Lucene(dynamic shape) + public IHtmlContent ContentPickerField_Option__Lucene(dynamic shape) #pragma warning restore CA1707 + { + var selected = shape.Editor == "Lucene"; + if (selected) { - var selected = shape.Editor == "Lucene"; - if (selected) - { - return new HtmlString($""); - } - return new HtmlString($""); + return new HtmlString($""); } + return new HtmlString($""); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Startup.cs index 70845b2ce49..4db6f1ee0d4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/Startup.cs @@ -27,92 +27,91 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Search.Lucene +namespace OrchardCore.Search.Lucene; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddDataMigration(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.Configure(o => - o.Analyzers.Add(new LuceneAnalyzer(LuceneSettings.StandardAnalyzer, - new StandardAnalyzer(LuceneSettings.DefaultVersion)))); + services.Configure(o => + o.Analyzers.Add(new LuceneAnalyzer(LuceneSettings.StandardAnalyzer, + new StandardAnalyzer(LuceneSettings.DefaultVersion)))); - services.AddScoped, LuceneQueryDisplayDriver>(); - services.AddScoped(); + services.AddScoped, LuceneQueryDisplayDriver>(); + services.AddScoped(); - services.AddLuceneQueries() - .AddQuerySource(LuceneQuerySource.SourceName); + services.AddLuceneQueries() + .AddQuerySource(LuceneQuerySource.SourceName); - services.AddRecipeExecutionStep(); - services.AddRecipeExecutionStep(); - services.AddRecipeExecutionStep(); - services.AddScoped(); - services.AddDataMigration(); - services.AddScoped(); - } + services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); + services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Search")] - public sealed class SearchStartup : StartupBase +[RequireFeatures("OrchardCore.Search")] +public sealed class SearchStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, LuceneSettingsDisplayDriver>(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped, LuceneSettingsDisplayDriver>(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - services.AddDeployment(); - services.AddDeployment(); - services.AddDeployment(); - } + services.AddDeployment(); + services.AddDeployment(); + services.AddDeployment(); + services.AddDeployment(); } +} - [Feature("OrchardCore.Search.Lucene.Worker")] - public sealed class LuceneWorkerStartup : StartupBase +[Feature("OrchardCore.Search.Lucene.Worker")] +public sealed class LuceneWorkerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddSingleton(); } +} - [Feature("OrchardCore.Search.Lucene.ContentPicker")] - public sealed class LuceneContentPickerStartup : StartupBase +[Feature("OrchardCore.Search.Lucene.ContentPicker")] +public sealed class LuceneContentPickerStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddShapeAttributes(); - } + services.AddScoped(); + services.AddScoped(); + services.AddShapeAttributes(); } +} - [RequireFeatures("OrchardCore.ContentTypes")] - public sealed class ContentTypesStartup : StartupBase +[RequireFeatures("OrchardCore.ContentTypes")] +public sealed class ContentTypesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminIndexViewModel.cs index 03ffde772b5..381c9d1a842 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminIndexViewModel.cs @@ -2,37 +2,36 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class AdminIndexViewModel { - public class AdminIndexViewModel - { - public IEnumerable Indexes { get; set; } + public IEnumerable Indexes { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); + public ContentOptions Options { get; set; } = new ContentOptions(); - [BindNever] - public dynamic Pager { get; set; } - } + [BindNever] + public dynamic Pager { get; set; } +} - public class ContentOptions - { - public ContentsBulkAction BulkAction { get; set; } +public class ContentOptions +{ + public ContentsBulkAction BulkAction { get; set; } - public string Search { get; set; } + public string Search { get; set; } - #region Lists to populate + #region Lists to populate - [BindNever] - public List ContentsBulkAction { get; set; } + [BindNever] + public List ContentsBulkAction { get; set; } - #endregion Lists to populate - } + #endregion Lists to populate +} - public enum ContentsBulkAction - { - None, - Reset, - Rebuild, - Remove - } +public enum ContentsBulkAction +{ + None, + Reset, + Rebuild, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminQueryViewModel.cs index 74694a8a570..61f33b6f2da 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/AdminQueryViewModel.cs @@ -3,24 +3,23 @@ using Lucene.Net.Documents; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class AdminQueryViewModel { - public class AdminQueryViewModel - { - public string DecodedQuery { get; set; } - public string IndexName { get; set; } - public string Parameters { get; set; } + public string DecodedQuery { get; set; } + public string IndexName { get; set; } + public string Parameters { get; set; } - [BindNever] - public int Count { get; set; } + [BindNever] + public int Count { get; set; } - [BindNever] - public string[] Indices { get; set; } + [BindNever] + public string[] Indices { get; set; } - [BindNever] - public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; + [BindNever] + public TimeSpan Elapsed { get; set; } = TimeSpan.Zero; - [BindNever] - public IEnumerable Documents { get; set; } = []; - } + [BindNever] + public IEnumerable Documents { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/IndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/IndexViewModel.cs index 913ad71c997..ad14879f254 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/IndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/IndexViewModel.cs @@ -1,11 +1,10 @@ using System; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class IndexViewModel { - public class IndexViewModel - { - public string Name { get; set; } - public string AnalyzerName { get; set; } - public DateTime LastUpdateUtc { get; set; } - } + public string Name { get; set; } + public string AnalyzerName { get; set; } + public DateTime LastUpdateUtc { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneContentIndexSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneContentIndexSettingsViewModel.cs index 4f2b08dbe57..2aa3074dd75 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneContentIndexSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneContentIndexSettingsViewModel.cs @@ -1,9 +1,8 @@ using OrchardCore.Search.Lucene.Model; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneContentIndexSettingsViewModel { - public class LuceneContentIndexSettingsViewModel - { - public LuceneContentIndexSettings LuceneContentIndexSettings { get; set; } - } + public LuceneContentIndexSettings LuceneContentIndexSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexDeploymentStepViewModel.cs index 388d798a89a..a0b3f826237 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneIndexDeploymentStepViewModel { - public class LuceneIndexDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexRebuildDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexRebuildDeploymentStepViewModel.cs index 71864a638bd..93c9b9fc0f2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexRebuildDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexRebuildDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneIndexRebuildDeploymentStepViewModel { - public class LuceneIndexRebuildDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexResetDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexResetDeploymentStepViewModel.cs index da3a86f1127..5a8eac15d25 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexResetDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexResetDeploymentStepViewModel.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneIndexResetDeploymentStepViewModel { - public class LuceneIndexResetDeploymentStepViewModel - { - public bool IncludeAll { get; set; } - public string[] IndexNames { get; set; } - public string[] AllIndexNames { get; set; } - } + public bool IncludeAll { get; set; } + public string[] IndexNames { get; set; } + public string[] AllIndexNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexSettingsViewModel.cs index 33e25b7047c..8731ce006b0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneIndexSettingsViewModel.cs @@ -2,32 +2,31 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneIndexSettingsViewModel { - public class LuceneIndexSettingsViewModel - { - public string IndexName { get; set; } + public string IndexName { get; set; } - public string AnalyzerName { get; set; } + public string AnalyzerName { get; set; } - public bool IndexLatest { get; set; } + public bool IndexLatest { get; set; } - public string Culture { get; set; } + public string Culture { get; set; } - public string[] IndexedContentTypes { get; set; } + public string[] IndexedContentTypes { get; set; } - public bool IsCreate { get; set; } + public bool IsCreate { get; set; } - public bool StoreSourceData { get; set; } + public bool StoreSourceData { get; set; } - #region List to populate + #region List to populate - [BindNever] - public IEnumerable Analyzers { get; set; } + [BindNever] + public IEnumerable Analyzers { get; set; } - [BindNever] - public IEnumerable Cultures { get; set; } + [BindNever] + public IEnumerable Cultures { get; set; } - #endregion List to populate - } + #endregion List to populate } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneQueryViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneQueryViewModel.cs index 09c5567cc54..b29ea658989 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneQueryViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneQueryViewModel.cs @@ -1,10 +1,9 @@ -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneQueryViewModel { - public class LuceneQueryViewModel - { - public string[] Indices { get; set; } - public string Index { get; set; } - public string Query { get; set; } - public bool ReturnContentItems { get; set; } - } + public string[] Indices { get; set; } + public string Index { get; set; } + public string Query { get; set; } + public bool ReturnContentItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneSettingsViewModel.cs index 2e591c59686..71c60a7702a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/LuceneSettingsViewModel.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class LuceneSettingsViewModel { - public class LuceneSettingsViewModel - { - public string Analyzer { get; set; } - public string SearchIndex { get; set; } - public IEnumerable SearchIndexes { get; set; } - public string SearchFields { get; set; } - public bool AllowLuceneQueriesInSearch { get; set; } - } + public string Analyzer { get; set; } + public string SearchIndex { get; set; } + public IEnumerable SearchIndexes { get; set; } + public string SearchFields { get; set; } + public bool AllowLuceneQueriesInSearch { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/QueryIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/QueryIndexViewModel.cs index 8e3d9cba608..06c1e31e767 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/QueryIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search.Lucene/ViewModels/QueryIndexViewModel.cs @@ -3,17 +3,16 @@ using Lucene.Net.Documents; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace OrchardCore.Search.Lucene.ViewModels +namespace OrchardCore.Search.Lucene.ViewModels; + +public class QueryIndexViewModel { - public class QueryIndexViewModel - { - public string Query { get; set; } - public string IndexName { get; set; } + public string Query { get; set; } + public string IndexName { get; set; } - [BindNever] - public TimeSpan Duration { get; set; } + [BindNever] + public TimeSpan Duration { get; set; } - [BindNever] - public IEnumerable Documents { get; set; } = []; - } + [BindNever] + public IEnumerable Documents { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Search/AdminMenu.cs index c1a3be29143..8fe4f9a1135 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/AdminMenu.cs @@ -3,44 +3,43 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Search +namespace OrchardCore.Search; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", SearchConstants.SearchSettingsGroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", SearchConstants.SearchSettingsGroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Search"], NavigationConstants.AdminMenuSearchPosition, search => search - .AddClass("search") - .Id("search") - .Add(S["Settings"], S["Settings"].PrefixPosition(), settings => settings - .Action("Index", "Admin", _routeValues) - .AddClass("searchsettings") - .Id("searchsettings") - .Permission(Permissions.ManageSearchSettings) - .LocalNav() - ) - ); - return Task.CompletedTask; } + + builder + .Add(S["Search"], NavigationConstants.AdminMenuSearchPosition, search => search + .AddClass("search") + .Id("search") + .Add(S["Settings"], S["Settings"].PrefixPosition(), settings => settings + .Action("Index", "Admin", _routeValues) + .AddClass("searchsettings") + .Id("searchsettings") + .Permission(Permissions.ManageSearchSettings) + .LocalNav() + ) + ); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentSource.cs index 9d1c201a7a6..af19dad1773 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentSource.cs @@ -4,31 +4,30 @@ using OrchardCore.Search.Models; using OrchardCore.Settings; -namespace OrchardCore.Search.Deployment +namespace OrchardCore.Search.Deployment; + +public class SearchSettingsDeploymentSource : IDeploymentSource { - public class SearchSettingsDeploymentSource : IDeploymentSource + private readonly ISiteService _siteService; + + public SearchSettingsDeploymentSource(ISiteService site) { - private readonly ISiteService _siteService; + _siteService = site; + } - public SearchSettingsDeploymentSource(ISiteService site) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not SearchSettingsDeploymentStep) { - _siteService = site; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not SearchSettingsDeploymentStep) - { - return; - } - - var searchSettings = await _siteService.GetSettingsAsync(); + var searchSettings = await _siteService.GetSettingsAsync(); - result.Steps.Add(new JsonObject - { - ["name"] = "Settings", - ["SearchSettings"] = JObject.FromObject(searchSettings), - }); - } + result.Steps.Add(new JsonObject + { + ["name"] = "Settings", + ["SearchSettings"] = JObject.FromObject(searchSettings), + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStep.cs index e0eca39d37a..e8362236c3e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStep.cs @@ -1,15 +1,14 @@ using OrchardCore.Deployment; -namespace OrchardCore.Search.Deployment +namespace OrchardCore.Search.Deployment; + +/// +/// Adds layers to a . +/// +public class SearchSettingsDeploymentStep : DeploymentStep { - /// - /// Adds layers to a . - /// - public class SearchSettingsDeploymentStep : DeploymentStep + public SearchSettingsDeploymentStep() { - public SearchSettingsDeploymentStep() - { - Name = "SearchSettings"; - } + Name = "SearchSettings"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStepDriver.cs index b166778b1c9..155710f10e9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Deployment/SearchSettingsDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Search.Deployment +namespace OrchardCore.Search.Deployment; + +public sealed class SearchSettingsDeploymentStepDriver : DisplayDriver { - public sealed class SearchSettingsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(SearchSettingsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(SearchSettingsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("SearchSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("SearchSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("SearchSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("SearchSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(SearchSettingsDeploymentStep step, BuildEditorContext context) - { - return View("SearchSettingsDeploymentStep_Fields_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(SearchSettingsDeploymentStep step, BuildEditorContext context) + { + return View("SearchSettingsDeploymentStep_Fields_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Drivers/SearchSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Search/Drivers/SearchSettingsDisplayDriver.cs index 2c28ce66707..0c6abba8908 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Drivers/SearchSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Drivers/SearchSettingsDisplayDriver.cs @@ -13,70 +13,69 @@ using OrchardCore.Search.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Search.Drivers +namespace OrchardCore.Search.Drivers; + +public sealed class SearchSettingsDisplayDriver : SiteDisplayDriver { - public sealed class SearchSettingsDisplayDriver : SiteDisplayDriver + [Obsolete("This property should not be used. Instead use SearchConstants.SearchSettingsGroupId.")] + public const string GroupId = SearchConstants.SearchSettingsGroupId; + + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly IServiceProvider _serviceProvider; + + public SearchSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IServiceProvider serviceProvider + ) { - [Obsolete("This property should not be used. Instead use SearchConstants.SearchSettingsGroupId.")] - public const string GroupId = SearchConstants.SearchSettingsGroupId; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _serviceProvider = serviceProvider; + } - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly IServiceProvider _serviceProvider; + protected override string SettingsGroupId + => SearchConstants.SearchSettingsGroupId; - public SearchSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IServiceProvider serviceProvider - ) + public override async Task EditAsync(ISite site, SearchSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageSearchSettings)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _serviceProvider = serviceProvider; + return null; } - protected override string SettingsGroupId - => SearchConstants.SearchSettingsGroupId; - - public override async Task EditAsync(ISite site, SearchSettings settings, BuildEditorContext context) + return Initialize("SearchSettings_Edit", model => { - var user = _httpContextAccessor.HttpContext?.User; + var searchServices = _serviceProvider.GetServices(); - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageSearchSettings)) - { - return null; - } - - return Initialize("SearchSettings_Edit", model => - { - var searchServices = _serviceProvider.GetServices(); + model.SearchServices = searchServices.Select(service => new SelectListItem(service.Name, service.Name)).ToList(); + model.Placeholder = settings.Placeholder; + model.PageTitle = settings.PageTitle; + model.ProviderName = settings.ProviderName; + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - model.SearchServices = searchServices.Select(service => new SelectListItem(service.Name, service.Name)).ToList(); - model.Placeholder = settings.Placeholder; - model.PageTitle = settings.PageTitle; - model.ProviderName = settings.ProviderName; - }).Location("Content:2") - .OnGroup(SettingsGroupId); - } + public override async Task UpdateAsync(ISite site, SearchSettings section, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; - public override async Task UpdateAsync(ISite site, SearchSettings section, UpdateEditorContext context) + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageSearchSettings)) { - var user = _httpContextAccessor.HttpContext?.User; - - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageSearchSettings)) - { - return null; - } + return null; + } - var model = new SearchSettingsViewModel(); + var model = new SearchSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - section.ProviderName = model.ProviderName; - section.Placeholder = model.Placeholder; - section.PageTitle = model.PageTitle; + section.ProviderName = model.ProviderName; + section.Placeholder = model.Placeholder; + section.PageTitle = model.PageTitle; - return await EditAsync(site, section, context); - } + return await EditAsync(site, section, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Model/SearchSettings.cs b/src/OrchardCore.Modules/OrchardCore.Search/Model/SearchSettings.cs index 98cb737ebe5..e366f278854 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Model/SearchSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Model/SearchSettings.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Search.Models +namespace OrchardCore.Search.Models; + +public class SearchSettings { - public class SearchSettings - { - public string ProviderName { get; set; } + public string ProviderName { get; set; } - public string Placeholder { get; set; } + public string Placeholder { get; set; } - public string PageTitle { get; set; } - } + public string PageTitle { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Services/SearchSettingsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Search/Services/SearchSettingsConfiguration.cs index c632e5555ed..76e221c42df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Services/SearchSettingsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Services/SearchSettingsConfiguration.cs @@ -2,24 +2,23 @@ using OrchardCore.Search.Models; using OrchardCore.Settings; -namespace OrchardCore.Search.Configuration +namespace OrchardCore.Search.Configuration; + +public sealed class SearchSettingsConfiguration : IConfigureOptions { - public sealed class SearchSettingsConfiguration : IConfigureOptions - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public SearchSettingsConfiguration(ISiteService site) - { - _siteService = site; - } + public SearchSettingsConfiguration(ISiteService site) + { + _siteService = site; + } - public void Configure(SearchSettings options) - { - var settings = _siteService.GetSettingsAsync() - .GetAwaiter() - .GetResult(); + public void Configure(SearchSettings options) + { + var settings = _siteService.GetSettingsAsync() + .GetAwaiter() + .GetResult(); - options.ProviderName = settings.ProviderName; - } + options.ProviderName = settings.ProviderName; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Search/Startup.cs index 0d066865872..33819e18b65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/Startup.cs @@ -21,57 +21,56 @@ using OrchardCore.Security.Permissions; using OrchardCore.Settings; -namespace OrchardCore.Search +namespace OrchardCore.Search; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient, SearchSettingsConfiguration>(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, SearchSettingsDisplayDriver>(); + services.AddTransient, SearchSettingsConfiguration>(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, SearchSettingsDisplayDriver>(); - services.AddContentPart() - .UseDisplayDriver(); + services.AddContentPart() + .UseDisplayDriver(); - services.AddDataMigration(); - } + services.AddDataMigration(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapAreaControllerRoute( - name: "Search", - areaName: "OrchardCore.Search", - pattern: "search/{index?}", - defaults: new { controller = typeof(SearchController).ControllerName(), action = nameof(SearchController.Search) } - ); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + routes.MapAreaControllerRoute( + name: "Search", + areaName: "OrchardCore.Search", + pattern: "search/{index?}", + defaults: new { controller = typeof(SearchController).ControllerName(), action = nameof(SearchController.Search) } + ); } +} - [RequireFeatures("OrchardCore.Deployment")] - public sealed class DeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment")] +public sealed class DeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } +} - [RequireFeatures("OrchardCore.Liquid")] - public sealed class LiquidStartup : StartupBase +[RequireFeatures("OrchardCore.Liquid")] +public sealed class LiquidStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - } + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Search/ViewModel/SearchSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Search/ViewModel/SearchSettingsViewModel.cs index ff51f450ae7..eca2c9f5c5b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Search/ViewModel/SearchSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Search/ViewModel/SearchSettingsViewModel.cs @@ -2,17 +2,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Search.ViewModels +namespace OrchardCore.Search.ViewModels; + +public class SearchSettingsViewModel { - public class SearchSettingsViewModel - { - public string ProviderName { get; set; } + public string ProviderName { get; set; } - public string Placeholder { get; set; } + public string Placeholder { get; set; } - public string PageTitle { get; set; } + public string PageTitle { get; set; } - [BindNever] - public IList SearchServices { get; set; } - } + [BindNever] + public IList SearchServices { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Security/AdminMenu.cs index 71e84ce9b5f..0a79a029868 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/AdminMenu.cs @@ -4,45 +4,44 @@ using OrchardCore.Navigation; using OrchardCore.Security.Drivers; -namespace OrchardCore.Security +namespace OrchardCore.Security; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", SecuritySettingsDisplayDriver.GroupId }, + { "area", "OrchardCore.Settings" }, + { "groupId", SecuritySettingsDisplayDriver.GroupId }, + + }; - }; + internal readonly IStringLocalizer S; - internal readonly IStringLocalizer S; + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Security"], NavigationConstants.AdminMenuSecurityPosition, security => security - .AddClass("security") - .Id("security") - .Add(S["Settings"], settings => settings - .Add(S["Security Headers"], S["Security Headers"].PrefixPosition(), headers => headers - .Permission(SecurityPermissions.ManageSecurityHeadersSettings) - .Action("Index", "Admin", _routeValues) - .LocalNav() - ) + builder + .Add(S["Security"], NavigationConstants.AdminMenuSecurityPosition, security => security + .AddClass("security") + .Id("security") + .Add(S["Settings"], settings => settings + .Add(S["Security Headers"], S["Security Headers"].PrefixPosition(), headers => headers + .Permission(SecurityPermissions.ManageSecurityHeadersSettings) + .Action("Index", "Admin", _routeValues) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Drivers/SecuritySettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Security/Drivers/SecuritySettingsDisplayDriver.cs index d2b794ae071..1d6d0dd073c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Drivers/SecuritySettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Drivers/SecuritySettingsDisplayDriver.cs @@ -11,110 +11,109 @@ using OrchardCore.Security.ViewModels; using OrchardCore.Settings; -namespace OrchardCore.Security.Drivers +namespace OrchardCore.Security.Drivers; + +public sealed class SecuritySettingsDisplayDriver : SiteDisplayDriver { - public sealed class SecuritySettingsDisplayDriver : SiteDisplayDriver - { - internal const string GroupId = "SecurityHeaders"; + internal const string GroupId = "SecurityHeaders"; + + private readonly IShellReleaseManager _shellReleaseManager; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly SecuritySettings _securitySettings; - private readonly IShellReleaseManager _shellReleaseManager; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly SecuritySettings _securitySettings; + protected override string SettingsGroupId + => GroupId; - protected override string SettingsGroupId - => GroupId; + public SecuritySettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + IOptionsSnapshot securitySettings) + { + _shellReleaseManager = shellReleaseManager; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _securitySettings = securitySettings.Value; + } - public SecuritySettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - IOptionsSnapshot securitySettings) + public override async Task EditAsync(ISite site, SecuritySettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, SecurityPermissions.ManageSecurityHeadersSettings)) { - _shellReleaseManager = shellReleaseManager; - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _securitySettings = securitySettings.Value; + return null; } - public override async Task EditAsync(ISite site, SecuritySettings settings, BuildEditorContext context) - { - var user = _httpContextAccessor.HttpContext?.User; + context.AddTenantReloadWarningWrapper(); - if (!await _authorizationService.AuthorizeAsync(user, SecurityPermissions.ManageSecurityHeadersSettings)) + return Initialize("SecurityHeadersSettings_Edit", model => + { + // Set the settings from configuration when AdminSettings are overridden via ConfigureSecuritySettings() + var currentSettings = settings; + if (_securitySettings.FromConfiguration) { - return null; + currentSettings = _securitySettings; } - context.AddTenantReloadWarningWrapper(); + model.FromConfiguration = currentSettings.FromConfiguration; + model.ContentSecurityPolicy = currentSettings.ContentSecurityPolicy; + model.PermissionsPolicy = currentSettings.PermissionsPolicy; + model.ReferrerPolicy = currentSettings.ReferrerPolicy; - return Initialize("SecurityHeadersSettings_Edit", model => - { - // Set the settings from configuration when AdminSettings are overridden via ConfigureSecuritySettings() - var currentSettings = settings; - if (_securitySettings.FromConfiguration) - { - currentSettings = _securitySettings; - } - - model.FromConfiguration = currentSettings.FromConfiguration; - model.ContentSecurityPolicy = currentSettings.ContentSecurityPolicy; - model.PermissionsPolicy = currentSettings.PermissionsPolicy; - model.ReferrerPolicy = currentSettings.ReferrerPolicy; - - model.EnableSandbox = currentSettings.ContentSecurityPolicy != null && - currentSettings.ContentSecurityPolicy.ContainsKey(ContentSecurityPolicyValue.Sandbox); - - model.UpgradeInsecureRequests = currentSettings.ContentSecurityPolicy != null && - currentSettings.ContentSecurityPolicy.ContainsKey(ContentSecurityPolicyValue.UpgradeInsecureRequests); - }).Location("Content:2") - .OnGroup(SettingsGroupId); - } + model.EnableSandbox = currentSettings.ContentSecurityPolicy != null && + currentSettings.ContentSecurityPolicy.ContainsKey(ContentSecurityPolicyValue.Sandbox); + + model.UpgradeInsecureRequests = currentSettings.ContentSecurityPolicy != null && + currentSettings.ContentSecurityPolicy.ContainsKey(ContentSecurityPolicyValue.UpgradeInsecureRequests); + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - public override async Task UpdateAsync(ISite site, SecuritySettings settings, UpdateEditorContext context) + public override async Task UpdateAsync(ISite site, SecuritySettings settings, UpdateEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + + if (!await _authorizationService.AuthorizeAsync(user, SecurityPermissions.ManageSecurityHeadersSettings)) { - var user = _httpContextAccessor.HttpContext?.User; + return null; + } - if (!await _authorizationService.AuthorizeAsync(user, SecurityPermissions.ManageSecurityHeadersSettings)) - { - return null; - } + var model = new SecuritySettingsViewModel(); - var model = new SecuritySettingsViewModel(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - await context.Updater.TryUpdateModelAsync(model, Prefix); + PrepareContentSecurityPolicyValues(model); - PrepareContentSecurityPolicyValues(model); + settings.ContentTypeOptions = SecurityHeaderDefaults.ContentTypeOptions; + settings.ContentSecurityPolicy = model.ContentSecurityPolicy; + settings.PermissionsPolicy = model.PermissionsPolicy; + settings.ReferrerPolicy = model.ReferrerPolicy; - settings.ContentTypeOptions = SecurityHeaderDefaults.ContentTypeOptions; - settings.ContentSecurityPolicy = model.ContentSecurityPolicy; - settings.PermissionsPolicy = model.PermissionsPolicy; - settings.ReferrerPolicy = model.ReferrerPolicy; + _shellReleaseManager.RequestRelease(); - _shellReleaseManager.RequestRelease(); + return await EditAsync(site, settings, context); + } - return await EditAsync(site, settings, context); + private static void PrepareContentSecurityPolicyValues(SecuritySettingsViewModel model) + { + if (!model.EnableSandbox) + { + model.ContentSecurityPolicy.Remove(ContentSecurityPolicyValue.Sandbox); } - - private static void PrepareContentSecurityPolicyValues(SecuritySettingsViewModel model) + else if (!model.ContentSecurityPolicy.TryGetValue(ContentSecurityPolicyValue.Sandbox, out _)) { - if (!model.EnableSandbox) - { - model.ContentSecurityPolicy.Remove(ContentSecurityPolicyValue.Sandbox); - } - else if (!model.ContentSecurityPolicy.TryGetValue(ContentSecurityPolicyValue.Sandbox, out _)) - { - model.ContentSecurityPolicy[ContentSecurityPolicyValue.Sandbox] = null; - } + model.ContentSecurityPolicy[ContentSecurityPolicyValue.Sandbox] = null; + } - if (!model.UpgradeInsecureRequests) - { - model.ContentSecurityPolicy.Remove(ContentSecurityPolicyValue.UpgradeInsecureRequests); - } - else - { - model.ContentSecurityPolicy[ContentSecurityPolicyValue.UpgradeInsecureRequests] = null; - } + if (!model.UpgradeInsecureRequests) + { + model.ContentSecurityPolicy.Remove(ContentSecurityPolicyValue.UpgradeInsecureRequests); + } + else + { + model.ContentSecurityPolicy[ContentSecurityPolicyValue.UpgradeInsecureRequests] = null; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Extensions/OrchardCoreBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Security/Extensions/OrchardCoreBuilderExtensions.cs index 445ffc4fbb3..e649bc3a8bb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Extensions/OrchardCoreBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Extensions/OrchardCoreBuilderExtensions.cs @@ -2,28 +2,27 @@ using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Security.Settings; -namespace Microsoft.Extensions.DependencyInjection +namespace Microsoft.Extensions.DependencyInjection; + +public static class OrchardCoreBuilderExtensions { - public static class OrchardCoreBuilderExtensions + public static OrchardCoreBuilder ConfigureSecuritySettings(this OrchardCoreBuilder builder) { - public static OrchardCoreBuilder ConfigureSecuritySettings(this OrchardCoreBuilder builder) + builder.ConfigureServices((tenantServices, serviceProvider) => { - builder.ConfigureServices((tenantServices, serviceProvider) => - { - var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Security"); + var configurationSection = serviceProvider.GetRequiredService().GetSection("OrchardCore_Security"); - tenantServices.PostConfigure(settings => - { - settings.ContentSecurityPolicy.Clear(); - settings.PermissionsPolicy.Clear(); + tenantServices.PostConfigure(settings => + { + settings.ContentSecurityPolicy.Clear(); + settings.PermissionsPolicy.Clear(); - configurationSection.Bind(settings); + configurationSection.Bind(settings); - settings.FromConfiguration = true; - }); + settings.FromConfiguration = true; }); + }); - return builder; - } + return builder; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Extensions/SecurityHeadersApplicationBuilderExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Security/Extensions/SecurityHeadersApplicationBuilderExtensions.cs index e51d3c1919e..636880752f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Extensions/SecurityHeadersApplicationBuilderExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Extensions/SecurityHeadersApplicationBuilderExtensions.cs @@ -2,37 +2,36 @@ using OrchardCore.Security.Options; using OrchardCore.Security.Services; -namespace Microsoft.AspNetCore.Builder +namespace Microsoft.AspNetCore.Builder; + +public static class SecurityHeadersApplicationBuilderExtensions { - public static class SecurityHeadersApplicationBuilderExtensions + public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app) { - public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app) - { - ArgumentNullException.ThrowIfNull(app); + ArgumentNullException.ThrowIfNull(app); - return app.UseSecurityHeaders(new SecurityHeadersOptions()); - } + return app.UseSecurityHeaders(new SecurityHeadersOptions()); + } - public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app, SecurityHeadersOptions options) - { - ArgumentNullException.ThrowIfNull(app); - ArgumentNullException.ThrowIfNull(options); + public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app, SecurityHeadersOptions options) + { + ArgumentNullException.ThrowIfNull(app); + ArgumentNullException.ThrowIfNull(options); - app.UseMiddleware(options); + app.UseMiddleware(options); - return app; - } + return app; + } - public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app, Action optionsAction) - { - ArgumentNullException.ThrowIfNull(app); - ArgumentNullException.ThrowIfNull(optionsAction); + public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app, Action optionsAction) + { + ArgumentNullException.ThrowIfNull(app); + ArgumentNullException.ThrowIfNull(optionsAction); - var options = new SecurityHeadersOptions(); + var options = new SecurityHeadersOptions(); - optionsAction.Invoke(options); + optionsAction.Invoke(options); - return app.UseSecurityHeaders(options); - } + return app.UseSecurityHeaders(options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyOriginValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyOriginValue.cs index c55a98337f1..97076aa7850 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyOriginValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyOriginValue.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class ContentSecurityPolicyOriginValue { - public class ContentSecurityPolicyOriginValue - { - public const string Any = "*"; + public const string Any = "*"; - public const string None = "'none'"; + public const string None = "'none'"; - public const string Self = "'self'"; - } + public const string Self = "'self'"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyValue.cs index 8259ec15e5d..d3fe1738379 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentSecurityPolicyValue.cs @@ -1,39 +1,38 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class ContentSecurityPolicyValue { - public class ContentSecurityPolicyValue - { - public const string BaseUri = "base-uri"; + public const string BaseUri = "base-uri"; - public const string ChildSource = "child-src"; + public const string ChildSource = "child-src"; - public const string ConnectSource = "connect-src"; + public const string ConnectSource = "connect-src"; - public const string DefaultSource = "default-src"; + public const string DefaultSource = "default-src"; - public const string FontSource = "font-src"; + public const string FontSource = "font-src"; - public const string FormAction = "form-action"; + public const string FormAction = "form-action"; - public const string FrameAncestors = "frame-ancestors"; + public const string FrameAncestors = "frame-ancestors"; - public const string FrameSource = "frame-src"; + public const string FrameSource = "frame-src"; - public const string ImageSource = "img-src"; + public const string ImageSource = "img-src"; - public const string ManifestSource = "manifest-src"; + public const string ManifestSource = "manifest-src"; - public const string MediaSource = "media-src"; + public const string MediaSource = "media-src"; - public const string ObjectSource = "object-src"; + public const string ObjectSource = "object-src"; - public const string ReportUri = "report-uri"; + public const string ReportUri = "report-uri"; - public const string Sandbox = "sandbox"; + public const string Sandbox = "sandbox"; - public const string ScriptSource = "script-src"; + public const string ScriptSource = "script-src"; - public const string StyleSource = "style-src"; + public const string StyleSource = "style-src"; - public const string UpgradeInsecureRequests = "upgrade-insecure-requests"; - } + public const string UpgradeInsecureRequests = "upgrade-insecure-requests"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentTypeOptionsValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentTypeOptionsValue.cs index b2e6115bb8e..f8bad1a017f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentTypeOptionsValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/ContentTypeOptionsValue.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class ContentTypeOptionsValue { - public class ContentTypeOptionsValue - { - public const string NoSniff = "nosniff"; - } + public const string NoSniff = "nosniff"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyOriginValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyOriginValue.cs index 09f7d7585e8..2b74f4ef5aa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyOriginValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyOriginValue.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class PermissionsPolicyOriginValue { - public class PermissionsPolicyOriginValue - { - public const string Any = "*"; + public const string Any = "*"; - public const string None = "()"; + public const string None = "()"; - public const string Self = "self"; - } + public const string Self = "self"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyValue.cs index 3203daadefd..077714d6e95 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/PermissionsPolicyValue.cs @@ -1,65 +1,64 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class PermissionsPolicyValue { - public class PermissionsPolicyValue - { - public const string Accelerometer = "accelerometer"; + public const string Accelerometer = "accelerometer"; - public const string AmbientLightSensor = "ambient-light-sensor"; + public const string AmbientLightSensor = "ambient-light-sensor"; - public const string Autoplay = "autoplay"; + public const string Autoplay = "autoplay"; - public const string Battery = "battery"; + public const string Battery = "battery"; - public const string Camera = "camera"; + public const string Camera = "camera"; - public const string DisplayCapture = "display-capture"; + public const string DisplayCapture = "display-capture"; - public const string DocumentDomain = "document-domain"; + public const string DocumentDomain = "document-domain"; - public const string EncryptedMedia = "encrypted-media"; + public const string EncryptedMedia = "encrypted-media"; - public const string FullScreen = "fullscreen"; + public const string FullScreen = "fullscreen"; - public const string GamePad = "gamepad"; + public const string GamePad = "gamepad"; - public const string Geolocation = "geolocation"; + public const string Geolocation = "geolocation"; - public const string Gyroscope = "gyroscope"; + public const string Gyroscope = "gyroscope"; - public const string LayoutAnimations = "layout-animations"; + public const string LayoutAnimations = "layout-animations"; - public const string LegacyImageFormat = "legacy-image-format"; + public const string LegacyImageFormat = "legacy-image-format"; - public const string Magnetometer = "magnetometer"; + public const string Magnetometer = "magnetometer"; - public const string Microphone = "microphone"; + public const string Microphone = "microphone"; - public const string Midi = "midi"; + public const string Midi = "midi"; - public const string OversizedImages = "oversized-images"; + public const string OversizedImages = "oversized-images"; - public const string Payment = "payment"; + public const string Payment = "payment"; - public const string PictureInPicture = "picture-in-picture"; + public const string PictureInPicture = "picture-in-picture"; - public const string PublicKeyRetrieval = "publickey-credentials-get"; + public const string PublicKeyRetrieval = "publickey-credentials-get"; - public const string Push = "push"; + public const string Push = "push"; - public const string SpeakerSelection = "speaker-selection"; + public const string SpeakerSelection = "speaker-selection"; - public const string ScreenWakeLock = "screen-wake-lock"; + public const string ScreenWakeLock = "screen-wake-lock"; - public const string SyncXhr = "sync-xhr"; + public const string SyncXhr = "sync-xhr"; - public const string UnoptimizedImages = "unoptimized-images"; + public const string UnoptimizedImages = "unoptimized-images"; - public const string UnsizedMedia = "unsized-media"; + public const string UnsizedMedia = "unsized-media"; - public const string Usb = "usb"; + public const string Usb = "usb"; - public const string WebShare = "web-share"; + public const string WebShare = "web-share"; - public const string WebXR = "xr-spatial-tracking"; - } + public const string WebXR = "xr-spatial-tracking"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/ReferrerPolicyValue.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/ReferrerPolicyValue.cs index b2fc88e7c4c..87e86842f6b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/ReferrerPolicyValue.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/ReferrerPolicyValue.cs @@ -1,21 +1,20 @@ -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class ReferrerPolicyValue { - public class ReferrerPolicyValue - { - public const string NoReferrer = "no-referrer"; + public const string NoReferrer = "no-referrer"; - public const string NoReferrerWhenDowngrade = "no-referrer-when-downgrade"; + public const string NoReferrerWhenDowngrade = "no-referrer-when-downgrade"; - public const string Origin = "origin"; + public const string Origin = "origin"; - public const string OriginWhenCrossOrigin = "origin-when-cross-origin"; + public const string OriginWhenCrossOrigin = "origin-when-cross-origin"; - public const string SameOrigin = "same-origin"; + public const string SameOrigin = "same-origin"; - public const string StrictOrigin = "strict-origin"; + public const string StrictOrigin = "strict-origin"; - public const string StrictOriginWhenCrossOrigin = "strict-origin-when-cross-origin"; + public const string StrictOriginWhenCrossOrigin = "strict-origin-when-cross-origin"; - public const string UnsafeUrl = "unsafe-url"; - } + public const string UnsafeUrl = "unsafe-url"; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Options/SecurityHeadersOptions.cs b/src/OrchardCore.Modules/OrchardCore.Security/Options/SecurityHeadersOptions.cs index f09415cc030..17bcdc954e2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Options/SecurityHeadersOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Options/SecurityHeadersOptions.cs @@ -2,72 +2,71 @@ using System.Linq; using OrchardCore.Security.Services; -namespace OrchardCore.Security.Options +namespace OrchardCore.Security.Options; + +public class SecurityHeadersOptions { - public class SecurityHeadersOptions + public SecurityHeadersOptions() { - public SecurityHeadersOptions() - { - HeaderPolicyProviders = - [ - new ContentSecurityPolicyHeaderPolicyProvider { Options = this }, - new ContentTypeOptionsHeaderPolicyProvider { Options = this }, - new PermissionsHeaderPolicyProvider { Options = this }, - new ReferrerHeaderPolicyProvider { Options = this } - ]; - } - - public string[] ContentSecurityPolicy { get; set; } = SecurityHeaderDefaults.ContentSecurityPolicy; + HeaderPolicyProviders = + [ + new ContentSecurityPolicyHeaderPolicyProvider { Options = this }, + new ContentTypeOptionsHeaderPolicyProvider { Options = this }, + new PermissionsHeaderPolicyProvider { Options = this }, + new ReferrerHeaderPolicyProvider { Options = this } + ]; + } - public string ContentTypeOptions { get; set; } = SecurityHeaderDefaults.ContentTypeOptions; + public string[] ContentSecurityPolicy { get; set; } = SecurityHeaderDefaults.ContentSecurityPolicy; - public string[] PermissionsPolicy { get; set; } = SecurityHeaderDefaults.PermissionsPolicy; + public string ContentTypeOptions { get; set; } = SecurityHeaderDefaults.ContentTypeOptions; - public string ReferrerPolicy { get; set; } = SecurityHeaderDefaults.ReferrerPolicy; + public string[] PermissionsPolicy { get; set; } = SecurityHeaderDefaults.PermissionsPolicy; - public IList HeaderPolicyProviders { get; set; } + public string ReferrerPolicy { get; set; } = SecurityHeaderDefaults.ReferrerPolicy; - public SecurityHeadersOptions AddContentSecurityPolicy(Dictionary policies) - { - ContentSecurityPolicy = policies - .Where(kvp => kvp.Value != null || - kvp.Key == ContentSecurityPolicyValue.Sandbox || - kvp.Key == ContentSecurityPolicyValue.UpgradeInsecureRequests) - .Select(kvp => kvp.Key + (kvp.Value != null ? " " + kvp.Value : string.Empty)) - .ToArray(); + public IList HeaderPolicyProviders { get; set; } - return this; - } + public SecurityHeadersOptions AddContentSecurityPolicy(Dictionary policies) + { + ContentSecurityPolicy = policies + .Where(kvp => kvp.Value != null || + kvp.Key == ContentSecurityPolicyValue.Sandbox || + kvp.Key == ContentSecurityPolicyValue.UpgradeInsecureRequests) + .Select(kvp => kvp.Key + (kvp.Value != null ? " " + kvp.Value : string.Empty)) + .ToArray(); + + return this; + } - public SecurityHeadersOptions AddContentSecurityPolicy(params string[] policies) - { - ContentSecurityPolicy = policies; + public SecurityHeadersOptions AddContentSecurityPolicy(params string[] policies) + { + ContentSecurityPolicy = policies; - return this; - } + return this; + } - public SecurityHeadersOptions AddContentTypeOptions() - { - ContentTypeOptions = ContentTypeOptionsValue.NoSniff; + public SecurityHeadersOptions AddContentTypeOptions() + { + ContentTypeOptions = ContentTypeOptionsValue.NoSniff; - return this; - } + return this; + } - public SecurityHeadersOptions AddPermissionsPolicy(IDictionary policies) - { - PermissionsPolicy = policies - .Where(kvp => kvp.Value != PermissionsPolicyOriginValue.None) - .Select(kvp => kvp.Key + "=" + kvp.Value) - .ToArray(); + public SecurityHeadersOptions AddPermissionsPolicy(IDictionary policies) + { + PermissionsPolicy = policies + .Where(kvp => kvp.Value != PermissionsPolicyOriginValue.None) + .Select(kvp => kvp.Key + "=" + kvp.Value) + .ToArray(); - return this; - } + return this; + } - public SecurityHeadersOptions AddReferrerPolicy(string policy) - { - ReferrerPolicy = policy; + public SecurityHeadersOptions AddReferrerPolicy(string policy) + { + ReferrerPolicy = policy; - return this; - } + return this; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/SecurityHeaderDefaults.cs b/src/OrchardCore.Modules/OrchardCore.Security/SecurityHeaderDefaults.cs index e3da8387024..2ba55346c29 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/SecurityHeaderDefaults.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/SecurityHeaderDefaults.cs @@ -1,67 +1,66 @@ using OrchardCore.Security.Options; -namespace OrchardCore.Security +namespace OrchardCore.Security; + +public static class SecurityHeaderDefaults { - public static class SecurityHeaderDefaults - { - internal static readonly char PoliciesSeparator = ','; - internal static readonly string[] ContentSecurityPolicyNames = - [ - ContentSecurityPolicyValue.BaseUri, - ContentSecurityPolicyValue.ChildSource, - ContentSecurityPolicyValue.ConnectSource, - ContentSecurityPolicyValue.DefaultSource, - ContentSecurityPolicyValue.FontSource, - ContentSecurityPolicyValue.FormAction, - ContentSecurityPolicyValue.FrameAncestors, - ContentSecurityPolicyValue.FrameSource, - ContentSecurityPolicyValue.ImageSource, - ContentSecurityPolicyValue.ManifestSource, - ContentSecurityPolicyValue.MediaSource, - ContentSecurityPolicyValue.ObjectSource, - ContentSecurityPolicyValue.ScriptSource, - ContentSecurityPolicyValue.StyleSource, - ContentSecurityPolicyValue.Sandbox - ]; - internal static readonly string[] PermissionsPolicyNames = - [ - PermissionsPolicyValue.Accelerometer, - PermissionsPolicyValue.AmbientLightSensor, - PermissionsPolicyValue.Autoplay, - PermissionsPolicyValue.Battery, - PermissionsPolicyValue.Camera, - PermissionsPolicyValue.DisplayCapture, - PermissionsPolicyValue.DocumentDomain, - PermissionsPolicyValue.EncryptedMedia, - PermissionsPolicyValue.FullScreen, - PermissionsPolicyValue.GamePad, - PermissionsPolicyValue.Geolocation, - PermissionsPolicyValue.Gyroscope, - PermissionsPolicyValue.LayoutAnimations, - PermissionsPolicyValue.LegacyImageFormat, - PermissionsPolicyValue.Magnetometer, - PermissionsPolicyValue.Microphone, - PermissionsPolicyValue.Midi, - PermissionsPolicyValue.OversizedImages, - PermissionsPolicyValue.Payment, - PermissionsPolicyValue.PictureInPicture, - PermissionsPolicyValue.PublicKeyRetrieval, - PermissionsPolicyValue.SpeakerSelection, - PermissionsPolicyValue.ScreenWakeLock, - PermissionsPolicyValue.SyncXhr, - PermissionsPolicyValue.UnoptimizedImages, - PermissionsPolicyValue.UnsizedMedia, - PermissionsPolicyValue.Usb, - PermissionsPolicyValue.WebShare, - PermissionsPolicyValue.WebXR - ]; + internal static readonly char PoliciesSeparator = ','; + internal static readonly string[] ContentSecurityPolicyNames = + [ + ContentSecurityPolicyValue.BaseUri, + ContentSecurityPolicyValue.ChildSource, + ContentSecurityPolicyValue.ConnectSource, + ContentSecurityPolicyValue.DefaultSource, + ContentSecurityPolicyValue.FontSource, + ContentSecurityPolicyValue.FormAction, + ContentSecurityPolicyValue.FrameAncestors, + ContentSecurityPolicyValue.FrameSource, + ContentSecurityPolicyValue.ImageSource, + ContentSecurityPolicyValue.ManifestSource, + ContentSecurityPolicyValue.MediaSource, + ContentSecurityPolicyValue.ObjectSource, + ContentSecurityPolicyValue.ScriptSource, + ContentSecurityPolicyValue.StyleSource, + ContentSecurityPolicyValue.Sandbox + ]; + internal static readonly string[] PermissionsPolicyNames = + [ + PermissionsPolicyValue.Accelerometer, + PermissionsPolicyValue.AmbientLightSensor, + PermissionsPolicyValue.Autoplay, + PermissionsPolicyValue.Battery, + PermissionsPolicyValue.Camera, + PermissionsPolicyValue.DisplayCapture, + PermissionsPolicyValue.DocumentDomain, + PermissionsPolicyValue.EncryptedMedia, + PermissionsPolicyValue.FullScreen, + PermissionsPolicyValue.GamePad, + PermissionsPolicyValue.Geolocation, + PermissionsPolicyValue.Gyroscope, + PermissionsPolicyValue.LayoutAnimations, + PermissionsPolicyValue.LegacyImageFormat, + PermissionsPolicyValue.Magnetometer, + PermissionsPolicyValue.Microphone, + PermissionsPolicyValue.Midi, + PermissionsPolicyValue.OversizedImages, + PermissionsPolicyValue.Payment, + PermissionsPolicyValue.PictureInPicture, + PermissionsPolicyValue.PublicKeyRetrieval, + PermissionsPolicyValue.SpeakerSelection, + PermissionsPolicyValue.ScreenWakeLock, + PermissionsPolicyValue.SyncXhr, + PermissionsPolicyValue.UnoptimizedImages, + PermissionsPolicyValue.UnsizedMedia, + PermissionsPolicyValue.Usb, + PermissionsPolicyValue.WebShare, + PermissionsPolicyValue.WebXR + ]; - public static readonly string[] ContentSecurityPolicy = []; + public static readonly string[] ContentSecurityPolicy = []; - public static readonly string ContentTypeOptions = ContentTypeOptionsValue.NoSniff; + public static readonly string ContentTypeOptions = ContentTypeOptionsValue.NoSniff; - public static readonly string[] PermissionsPolicy = []; + public static readonly string[] PermissionsPolicy = []; - public static readonly string ReferrerPolicy = ReferrerPolicyValue.NoReferrer; - } + public static readonly string ReferrerPolicy = ReferrerPolicyValue.NoReferrer; } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentSecurityPolicyHeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentSecurityPolicyHeaderPolicyProvider.cs index 687206f8242..6bac57d1b7b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentSecurityPolicyHeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentSecurityPolicyHeaderPolicyProvider.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public class ContentSecurityPolicyHeaderPolicyProvider : HeaderPolicyProvider { - public class ContentSecurityPolicyHeaderPolicyProvider : HeaderPolicyProvider - { - private string _policy; + private string _policy; - public override void InitializePolicy() + public override void InitializePolicy() + { + if (Options.ContentSecurityPolicy.Length > 0) { - if (Options.ContentSecurityPolicy.Length > 0) - { - _policy = string.Join(SecurityHeaderDefaults.PoliciesSeparator, Options.ContentSecurityPolicy); - } + _policy = string.Join(SecurityHeaderDefaults.PoliciesSeparator, Options.ContentSecurityPolicy); } + } - public override void ApplyPolicy(HttpContext httpContext) + public override void ApplyPolicy(HttpContext httpContext) + { + if (_policy != null) { - if (_policy != null) - { - httpContext.Response.Headers[SecurityHeaderNames.ContentSecurityPolicy] = _policy; - } + httpContext.Response.Headers[SecurityHeaderNames.ContentSecurityPolicy] = _policy; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentTypeOptionsHeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentTypeOptionsHeaderPolicyProvider.cs index 59e6e8620ab..f023bcdba8b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentTypeOptionsHeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/ContentTypeOptionsHeaderPolicyProvider.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public class ContentTypeOptionsHeaderPolicyProvider : HeaderPolicyProvider { - public class ContentTypeOptionsHeaderPolicyProvider : HeaderPolicyProvider + public override void ApplyPolicy(HttpContext httpContext) { - public override void ApplyPolicy(HttpContext httpContext) - { - httpContext.Response.Headers[SecurityHeaderNames.XContentTypeOptions] = Options.ContentTypeOptions; - } + httpContext.Response.Headers[SecurityHeaderNames.XContentTypeOptions] = Options.ContentTypeOptions; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/HeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/HeaderPolicyProvider.cs index ef477dd612e..3b895fb66cf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/HeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/HeaderPolicyProvider.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Security.Options; -namespace OrchardCore.Security.Services -{ - public abstract class HeaderPolicyProvider : IHeaderPolicyProvider - { - public SecurityHeadersOptions Options { get; set; } +namespace OrchardCore.Security.Services; - public virtual void InitializePolicy() - { - // This is intentionally empty, only header policy provider(s) that need an initialization should override this - } +public abstract class HeaderPolicyProvider : IHeaderPolicyProvider +{ + public SecurityHeadersOptions Options { get; set; } - public abstract void ApplyPolicy(HttpContext httpContext); + public virtual void InitializePolicy() + { + // This is intentionally empty, only header policy provider(s) that need an initialization should override this } + + public abstract void ApplyPolicy(HttpContext httpContext); } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/IHeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/IHeaderPolicyProvider.cs index e07cb91319e..bf5f2ae2342 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/IHeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/IHeaderPolicyProvider.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public interface IHeaderPolicyProvider { - public interface IHeaderPolicyProvider - { - void InitializePolicy(); + void InitializePolicy(); - void ApplyPolicy(HttpContext httpContext); - } + void ApplyPolicy(HttpContext httpContext); } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/ISecurityService.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/ISecurityService.cs index e58a9c10cc8..0f780c2b0fd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/ISecurityService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/ISecurityService.cs @@ -1,10 +1,9 @@ using System.Threading.Tasks; using OrchardCore.Security.Settings; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public interface ISecurityService { - public interface ISecurityService - { - Task GetSettingsAsync(); - } + Task GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/PermissionsHeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/PermissionsHeaderPolicyProvider.cs index 72f842be3e3..83ef025a838 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/PermissionsHeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/PermissionsHeaderPolicyProvider.cs @@ -1,25 +1,24 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public class PermissionsHeaderPolicyProvider : HeaderPolicyProvider { - public class PermissionsHeaderPolicyProvider : HeaderPolicyProvider - { - private string _policy; + private string _policy; - public override void InitializePolicy() + public override void InitializePolicy() + { + if (Options.PermissionsPolicy.Length > 0) { - if (Options.PermissionsPolicy.Length > 0) - { - _policy = string.Join(SecurityHeaderDefaults.PoliciesSeparator, Options.PermissionsPolicy); - } + _policy = string.Join(SecurityHeaderDefaults.PoliciesSeparator, Options.PermissionsPolicy); } + } - public override void ApplyPolicy(HttpContext httpContext) + public override void ApplyPolicy(HttpContext httpContext) + { + if (_policy != null) { - if (_policy != null) - { - httpContext.Response.Headers[SecurityHeaderNames.PermissionsPolicy] = _policy; - } + httpContext.Response.Headers[SecurityHeaderNames.PermissionsPolicy] = _policy; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/ReferrerHeaderPolicyProvider.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/ReferrerHeaderPolicyProvider.cs index a3f3adf8873..b7f02bc5be2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/ReferrerHeaderPolicyProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/ReferrerHeaderPolicyProvider.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Http; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public class ReferrerHeaderPolicyProvider : HeaderPolicyProvider { - public class ReferrerHeaderPolicyProvider : HeaderPolicyProvider + public override void ApplyPolicy(HttpContext httpContext) { - public override void ApplyPolicy(HttpContext httpContext) - { - httpContext.Response.Headers[SecurityHeaderNames.ReferrerPolicy] = Options.ReferrerPolicy; - } + httpContext.Response.Headers[SecurityHeaderNames.ReferrerPolicy] = Options.ReferrerPolicy; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityHeadersMiddleware.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityHeadersMiddleware.cs index 5558cb4e5b0..2fdfb4d8246 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityHeadersMiddleware.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityHeadersMiddleware.cs @@ -2,32 +2,31 @@ using Microsoft.AspNetCore.Http; using OrchardCore.Security.Options; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public class SecurityHeadersMiddleware { - public class SecurityHeadersMiddleware + private readonly SecurityHeadersOptions _options; + private readonly RequestDelegate _next; + + public SecurityHeadersMiddleware(SecurityHeadersOptions options, RequestDelegate next) { - private readonly SecurityHeadersOptions _options; - private readonly RequestDelegate _next; + _options = options; + _next = next; - public SecurityHeadersMiddleware(SecurityHeadersOptions options, RequestDelegate next) + foreach (var provider in _options.HeaderPolicyProviders) { - _options = options; - _next = next; - - foreach (var provider in _options.HeaderPolicyProviders) - { - provider.InitializePolicy(); - } + provider.InitializePolicy(); } + } - public Task Invoke(HttpContext context) + public Task Invoke(HttpContext context) + { + foreach (var provider in _options.HeaderPolicyProviders) { - foreach (var provider in _options.HeaderPolicyProviders) - { - provider.ApplyPolicy(context); - } - - return _next.Invoke(context); + provider.ApplyPolicy(context); } + + return _next.Invoke(context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityService.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityService.cs index 11599cc8136..e3a045a7b3d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecurityService.cs @@ -2,18 +2,17 @@ using OrchardCore.Security.Settings; using OrchardCore.Settings; -namespace OrchardCore.Security.Services -{ - public class SecurityService : ISecurityService - { - private readonly ISiteService _siteService; +namespace OrchardCore.Security.Services; - public SecurityService(ISiteService siteService) - { - _siteService = siteService; - } +public class SecurityService : ISecurityService +{ + private readonly ISiteService _siteService; - public Task GetSettingsAsync() - => _siteService.GetSettingsAsync(); + public SecurityService(ISiteService siteService) + { + _siteService = siteService; } + + public Task GetSettingsAsync() + => _siteService.GetSettingsAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecuritySettingsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecuritySettingsConfiguration.cs index 52a7d1f8b12..01b287e9c1e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Services/SecuritySettingsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Services/SecuritySettingsConfiguration.cs @@ -1,24 +1,23 @@ using Microsoft.Extensions.Options; using OrchardCore.Security.Settings; -namespace OrchardCore.Security.Services +namespace OrchardCore.Security.Services; + +public sealed class SecuritySettingsConfiguration : IConfigureOptions { - public sealed class SecuritySettingsConfiguration : IConfigureOptions - { - private readonly ISecurityService _securityService; + private readonly ISecurityService _securityService; - public SecuritySettingsConfiguration(ISecurityService securityService) - => _securityService = securityService; + public SecuritySettingsConfiguration(ISecurityService securityService) + => _securityService = securityService; - public void Configure(SecuritySettings options) - { - var securitySettings = _securityService.GetSettingsAsync() - .GetAwaiter().GetResult(); + public void Configure(SecuritySettings options) + { + var securitySettings = _securityService.GetSettingsAsync() + .GetAwaiter().GetResult(); - options.ContentSecurityPolicy = securitySettings.ContentSecurityPolicy; - options.ContentTypeOptions = securitySettings.ContentTypeOptions; - options.PermissionsPolicy = securitySettings.PermissionsPolicy; - options.ReferrerPolicy = securitySettings.ReferrerPolicy; - } + options.ContentSecurityPolicy = securitySettings.ContentSecurityPolicy; + options.ContentTypeOptions = securitySettings.ContentTypeOptions; + options.PermissionsPolicy = securitySettings.PermissionsPolicy; + options.ReferrerPolicy = securitySettings.ReferrerPolicy; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Settings/SecuritySettings.cs b/src/OrchardCore.Modules/OrchardCore.Security/Settings/SecuritySettings.cs index 17318ead6ef..96148cc0115 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Settings/SecuritySettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Settings/SecuritySettings.cs @@ -2,58 +2,57 @@ using System.Linq; using OrchardCore.Security.Options; -namespace OrchardCore.Security.Settings +namespace OrchardCore.Security.Settings; + +public class SecuritySettings { - public class SecuritySettings - { - private Dictionary _contentSecurityPolicy = []; - private Dictionary _permissionsPolicy = []; + private Dictionary _contentSecurityPolicy = []; + private Dictionary _permissionsPolicy = []; - public Dictionary ContentSecurityPolicy + public Dictionary ContentSecurityPolicy + { + get => _contentSecurityPolicy; + set { - get => _contentSecurityPolicy; - set + if (value == null) + { + return; + } + + // Exclude null values and clone the dictionary to not be shared by site settings and options instances. + _contentSecurityPolicy = value + .Where(kvp => kvp.Value != null || + kvp.Key == ContentSecurityPolicyValue.Sandbox || + kvp.Key == ContentSecurityPolicyValue.UpgradeInsecureRequests) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + if (_contentSecurityPolicy.TryGetValue(ContentSecurityPolicyValue.UpgradeInsecureRequests, out _)) { - if (value == null) - { - return; - } - - // Exclude null values and clone the dictionary to not be shared by site settings and options instances. - _contentSecurityPolicy = value - .Where(kvp => kvp.Value != null || - kvp.Key == ContentSecurityPolicyValue.Sandbox || - kvp.Key == ContentSecurityPolicyValue.UpgradeInsecureRequests) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - - if (_contentSecurityPolicy.TryGetValue(ContentSecurityPolicyValue.UpgradeInsecureRequests, out _)) - { - _contentSecurityPolicy[ContentSecurityPolicyValue.UpgradeInsecureRequests] = null; - } + _contentSecurityPolicy[ContentSecurityPolicyValue.UpgradeInsecureRequests] = null; } } + } - public string ContentTypeOptions { get; set; } = SecurityHeaderDefaults.ContentTypeOptions; + public string ContentTypeOptions { get; set; } = SecurityHeaderDefaults.ContentTypeOptions; - public Dictionary PermissionsPolicy + public Dictionary PermissionsPolicy + { + get => _permissionsPolicy; + set { - get => _permissionsPolicy; - set + if (value == null) { - if (value == null) - { - return; - } - - // Exlude 'None' values and clone the dictionary to not be shared by site settings and options instances. - _permissionsPolicy = value - .Where(kvp => kvp.Value != PermissionsPolicyOriginValue.None) - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + return; } + + // Exlude 'None' values and clone the dictionary to not be shared by site settings and options instances. + _permissionsPolicy = value + .Where(kvp => kvp.Value != PermissionsPolicyOriginValue.None) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } + } - public string ReferrerPolicy { get; set; } = SecurityHeaderDefaults.ReferrerPolicy; + public string ReferrerPolicy { get; set; } = SecurityHeaderDefaults.ReferrerPolicy; - public bool FromConfiguration { get; set; } - } + public bool FromConfiguration { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Security/Startup.cs index 41c32163cf1..d5b9afef5ee 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/Startup.cs @@ -12,36 +12,35 @@ using OrchardCore.Security.Settings; using OrchardCore.Settings; -namespace OrchardCore.Security +namespace OrchardCore.Security; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override int Order + => OrchardCoreConstants.ConfigureOrder.Security; + + public override void ConfigureServices(IServiceCollection services) { - public override int Order - => OrchardCoreConstants.ConfigureOrder.Security; + services.AddScoped(); + services.AddScoped, SecuritySettingsDisplayDriver>(); + services.AddScoped(); - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, SecuritySettingsDisplayDriver>(); - services.AddScoped(); + services.AddSingleton(); - services.AddSingleton(); + services.AddTransient, SecuritySettingsConfiguration>(); + } - services.AddTransient, SecuritySettingsConfiguration>(); - } + public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var securityOptions = serviceProvider.GetRequiredService>().Value; - public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + builder.UseSecurityHeaders(options => { - var securityOptions = serviceProvider.GetRequiredService>().Value; - - builder.UseSecurityHeaders(options => - { - options - .AddContentSecurityPolicy(securityOptions.ContentSecurityPolicy) - .AddContentTypeOptions() - .AddPermissionsPolicy(securityOptions.PermissionsPolicy) - .AddReferrerPolicy(securityOptions.ReferrerPolicy); - }); - } + options + .AddContentSecurityPolicy(securityOptions.ContentSecurityPolicy) + .AddContentTypeOptions() + .AddPermissionsPolicy(securityOptions.PermissionsPolicy) + .AddReferrerPolicy(securityOptions.ReferrerPolicy); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Security/ViewModels/SecuritySettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Security/ViewModels/SecuritySettingsViewModel.cs index ea1a469addb..b0c69c1ab14 100644 --- a/src/OrchardCore.Modules/OrchardCore.Security/ViewModels/SecuritySettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Security/ViewModels/SecuritySettingsViewModel.cs @@ -3,48 +3,47 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Security.Options; -namespace OrchardCore.Security.ViewModels +namespace OrchardCore.Security.ViewModels; + +public class SecuritySettingsViewModel { - public class SecuritySettingsViewModel - { - private Dictionary _contentSecurityPolicy = []; - private Dictionary _permissionsPolicy = []; + private Dictionary _contentSecurityPolicy = []; + private Dictionary _permissionsPolicy = []; - public Dictionary ContentSecurityPolicy + public Dictionary ContentSecurityPolicy + { + get => _contentSecurityPolicy; + set { - get => _contentSecurityPolicy; - set - { - // Populate all policy values for the editor (null if not provided). - _contentSecurityPolicy = SecurityHeaderDefaults.ContentSecurityPolicyNames - .ToDictionary(name => name, name => - value?.ContainsKey(name) ?? false - ? value[name] - : null); - } + // Populate all policy values for the editor (null if not provided). + _contentSecurityPolicy = SecurityHeaderDefaults.ContentSecurityPolicyNames + .ToDictionary(name => name, name => + value?.ContainsKey(name) ?? false + ? value[name] + : null); } + } - public bool EnableSandbox { get; set; } + public bool EnableSandbox { get; set; } - public bool UpgradeInsecureRequests { get; set; } + public bool UpgradeInsecureRequests { get; set; } - public Dictionary PermissionsPolicy + public Dictionary PermissionsPolicy + { + get => _permissionsPolicy; + set { - get => _permissionsPolicy; - set - { - // Populate all policy values for the editor ('None' if not provided). - _permissionsPolicy = SecurityHeaderDefaults.PermissionsPolicyNames - .ToDictionary(name => name, name => - value?.ContainsKey(name) ?? false - ? value[name] - : PermissionsPolicyOriginValue.None); - } + // Populate all policy values for the editor ('None' if not provided). + _permissionsPolicy = SecurityHeaderDefaults.PermissionsPolicyNames + .ToDictionary(name => name, name => + value?.ContainsKey(name) ?? false + ? value[name] + : PermissionsPolicyOriginValue.None); } + } - public string ReferrerPolicy { get; set; } + public string ReferrerPolicy { get; set; } - [BindNever] - public bool FromConfiguration { get; set; } - } + [BindNever] + public bool FromConfiguration { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs index bceb1199e2d..8bce6b5fa91 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoContentDriver.cs @@ -14,287 +14,286 @@ using OrchardCore.Shortcodes.Services; using Shortcodes; -namespace OrchardCore.Seo.Drivers +namespace OrchardCore.Seo.Drivers; + +public sealed class SeoContentDriver : ContentDisplayDriver { - public sealed class SeoContentDriver : ContentDisplayDriver + private readonly IContentManager _contentManager; + private readonly IPageTitleBuilder _pageTitleBuilder; + private readonly IResourceManager _resourceManager; + private readonly IShortcodeService _shortcodeService; + private readonly HtmlEncoder _htmlEncoder; + + private bool _primaryContentRendered { get; set; } + + public SeoContentDriver( + IContentManager contentManager, + IPageTitleBuilder pageTitleBuilder, + IResourceManager resourceManager, + IShortcodeService shortcodeService, + HtmlEncoder htmlEncoder + ) + { + _contentManager = contentManager; + _pageTitleBuilder = pageTitleBuilder; + _resourceManager = resourceManager; + _shortcodeService = shortcodeService; + _htmlEncoder = htmlEncoder; + } + + public override async Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) { - private readonly IContentManager _contentManager; - private readonly IPageTitleBuilder _pageTitleBuilder; - private readonly IResourceManager _resourceManager; - private readonly IShortcodeService _shortcodeService; - private readonly HtmlEncoder _htmlEncoder; - - private bool _primaryContentRendered { get; set; } - - public SeoContentDriver( - IContentManager contentManager, - IPageTitleBuilder pageTitleBuilder, - IResourceManager resourceManager, - IShortcodeService shortcodeService, - HtmlEncoder htmlEncoder - ) + // We only apply this on the primary content item, which is considered the first call to BuildDisplay. + if (_primaryContentRendered) { - _contentManager = contentManager; - _pageTitleBuilder = pageTitleBuilder; - _resourceManager = resourceManager; - _shortcodeService = shortcodeService; - _htmlEncoder = htmlEncoder; + return null; } - public override async Task DisplayAsync(ContentItem contentItem, BuildDisplayContext context) + _primaryContentRendered = true; + + // Do not include Widgets or any display type other than detail. + if (context.DisplayType != "Detail" || context.Shape.TryGetProperty(nameof(ContentTypeSettings.Stereotype), out string _)) { - // We only apply this on the primary content item, which is considered the first call to BuildDisplay. - if (_primaryContentRendered) - { - return null; - } + return null; + } - _primaryContentRendered = true; + var aspect = await _contentManager.PopulateAspectAsync(contentItem); - // Do not include Widgets or any display type other than detail. - if (context.DisplayType != "Detail" || context.Shape.TryGetProperty(nameof(ContentTypeSettings.Stereotype), out string _)) - { - return null; - } + if (!aspect.Render) + { + return null; + } - var aspect = await _contentManager.PopulateAspectAsync(contentItem); + var shortCodeContext = new Context + { + ["ContentItem"] = contentItem + }; - if (!aspect.Render) - { - return null; - } + if (!string.IsNullOrEmpty(aspect.PageTitle)) + { + _pageTitleBuilder.SetFixedTitle(new HtmlString(_htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.PageTitle, shortCodeContext)))); + } - var shortCodeContext = new Context + if (!string.IsNullOrEmpty(aspect.MetaDescription)) + { + _resourceManager.RegisterMeta(new MetaEntry { - ["ContentItem"] = contentItem - }; + Name = "description", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaDescription, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.PageTitle)) + if (!string.IsNullOrEmpty(aspect.MetaKeywords)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _pageTitleBuilder.SetFixedTitle(new HtmlString(_htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.PageTitle, shortCodeContext)))); - } + Name = "keywords", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaKeywords, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.MetaDescription)) + if (!string.IsNullOrEmpty(aspect.Canonical)) + { + _resourceManager.RegisterLink(new LinkEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "description", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaDescription, shortCodeContext)) - }); - } + Href = aspect.Canonical, + Rel = "canonical" + }); + } - if (!string.IsNullOrEmpty(aspect.MetaKeywords)) + if (!string.IsNullOrEmpty(aspect.MetaRobots)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "keywords", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaKeywords, shortCodeContext)) - }); - } + Name = "robots", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaRobots, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.Canonical)) - { - _resourceManager.RegisterLink(new LinkEntry - { - Href = aspect.Canonical, - Rel = "canonical" - }); - } + foreach (var customMetaTag in aspect.CustomMetaTags) + { + // Generate a new meta entry as the builder is prepopulated. + _resourceManager.RegisterMeta(new MetaEntry( + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Name, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Property, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Content, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.HttpEquiv, shortCodeContext)), + _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Charset, shortCodeContext)))); + } - if (!string.IsNullOrEmpty(aspect.MetaRobots)) + // OpenGraph. + if (!string.IsNullOrEmpty(aspect.OpenGraphType)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "robots", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.MetaRobots, shortCodeContext)) - }); - } + Property = "og:type", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphType, shortCodeContext)) + }); + } - foreach (var customMetaTag in aspect.CustomMetaTags) + if (!string.IsNullOrEmpty(aspect.OpenGraphTitle)) + { + _resourceManager.RegisterMeta(new MetaEntry { - // Generate a new meta entry as the builder is prepopulated. - _resourceManager.RegisterMeta(new MetaEntry( - _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Name, shortCodeContext)), - _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Property, shortCodeContext)), - _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Content, shortCodeContext)), - _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.HttpEquiv, shortCodeContext)), - _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(customMetaTag.Charset, shortCodeContext)))); - } + Property = "og:title", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphTitle, shortCodeContext)) + }); + } - // OpenGraph. - if (!string.IsNullOrEmpty(aspect.OpenGraphType)) + if (!string.IsNullOrEmpty(aspect.OpenGraphDescription)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:type", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphType, shortCodeContext)) - }); - } + Property = "og:description", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphDescription, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphTitle)) + if (!string.IsNullOrEmpty(aspect.OpenGraphImage)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:title", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphTitle, shortCodeContext)) - }); - } + Property = "og:image", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImage, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphDescription)) + if (!string.IsNullOrEmpty(aspect.OpenGraphImageAlt)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:description", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphDescription, shortCodeContext)) - }); - } + Property = "og:image:alt", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImageAlt, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphImage)) + if (!string.IsNullOrEmpty(aspect.OpenGraphUrl)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:image", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImage, shortCodeContext)) - }); - } + Property = "og:url", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphUrl, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphImageAlt)) + if (!string.IsNullOrEmpty(aspect.OpenGraphSiteName)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:image:alt", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphImageAlt, shortCodeContext)) - }); - } + Property = "og:site_name", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphSiteName, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphUrl)) + if (!string.IsNullOrEmpty(aspect.OpenGraphAppId)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:url", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphUrl, shortCodeContext)) - }); - } + Property = "fb:app_id", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphAppId, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphSiteName)) + if (!string.IsNullOrEmpty(aspect.OpenGraphLocale)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:site_name", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphSiteName, shortCodeContext)) - }); - } + Property = "og:locale", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphLocale, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphAppId)) + // Twitter. + if (!string.IsNullOrEmpty(aspect.TwitterCard)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "fb:app_id", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphAppId, shortCodeContext)) - }); - } + Property = "twitter:card", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCard, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.OpenGraphLocale)) + if (!string.IsNullOrEmpty(aspect.TwitterSite)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "og:locale", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.OpenGraphLocale, shortCodeContext)) - }); - } + Property = "twitter:site", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterSite, shortCodeContext)) + }); + } - // Twitter. - if (!string.IsNullOrEmpty(aspect.TwitterCard)) + if (!string.IsNullOrEmpty(aspect.TwitterTitle)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "twitter:card", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCard, shortCodeContext)) - }); - } + Name = "twitter:title", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterTitle, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterSite)) + if (!string.IsNullOrEmpty(aspect.TwitterDescription)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Property = "twitter:site", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterSite, shortCodeContext)) - }); - } + Name = "twitter:description", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterDescription, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterTitle)) + if (!string.IsNullOrEmpty(aspect.TwitterImage)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:title", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterTitle, shortCodeContext)) - }); - } + Name = "twitter:image", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImage, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterDescription)) + if (!string.IsNullOrEmpty(aspect.TwitterImageAlt)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:description", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterDescription, shortCodeContext)) - }); - } + Name = "twitter:image:alt", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImageAlt, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterImage)) + if (!string.IsNullOrEmpty(aspect.TwitterCreator)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:image", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImage, shortCodeContext)) - }); - } + Name = "twitter:creator", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCreator, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterImageAlt)) + if (!string.IsNullOrEmpty(aspect.TwitterUrl)) + { + _resourceManager.RegisterMeta(new MetaEntry { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:image:alt", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterImageAlt, shortCodeContext)) - }); - } + Name = "twitter:url", + Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterUrl, shortCodeContext)) + }); + } - if (!string.IsNullOrEmpty(aspect.TwitterCreator)) - { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:creator", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterCreator, shortCodeContext)) - }); - } + if (!string.IsNullOrEmpty(aspect.GoogleSchema)) + { + var json = await _shortcodeService.ProcessAsync(aspect.GoogleSchema, shortCodeContext); - if (!string.IsNullOrEmpty(aspect.TwitterUrl)) + try { - _resourceManager.RegisterMeta(new MetaEntry - { - Name = "twitter:url", - Content = _htmlEncoder.Encode(await _shortcodeService.ProcessAsync(aspect.TwitterUrl, shortCodeContext)) - }); + // Validate json format + JsonDocument.Parse(json); } - - if (!string.IsNullOrEmpty(aspect.GoogleSchema)) + catch { - var json = await _shortcodeService.ProcessAsync(aspect.GoogleSchema, shortCodeContext); - - try - { - // Validate json format - JsonDocument.Parse(json); - } - catch - { - json = "{ \"error\": \"Invalid JSON content in SEO settings\" }"; - } - - _resourceManager.RegisterHeadScript(new HtmlString($"")); - + json = "{ \"error\": \"Invalid JSON content in SEO settings\" }"; } - return null; + _resourceManager.RegisterHeadScript(new HtmlString($"")); + } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoMetaPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoMetaPartDisplayDriver.cs index d58d92e1451..f1e6c12fded 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoMetaPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Drivers/SeoMetaPartDisplayDriver.cs @@ -11,130 +11,129 @@ using OrchardCore.Seo.Models; using OrchardCore.Seo.ViewModels; -namespace OrchardCore.Seo.Drivers +namespace OrchardCore.Seo.Drivers; + +public sealed class SeoMetaPartDisplayDriver : ContentPartDisplayDriver { - public sealed class SeoMetaPartDisplayDriver : ContentPartDisplayDriver + internal readonly IStringLocalizer S; + + public SeoMetaPartDisplayDriver(IStringLocalizer stringLocalizer) { - internal readonly IStringLocalizer S; + S = stringLocalizer; + } - public SeoMetaPartDisplayDriver(IStringLocalizer stringLocalizer) - { - S = stringLocalizer; - } + public override IDisplayResult Edit(SeoMetaPart part, BuildPartEditorContext context) + { + var settings = context.TypePartDefinition.GetSettings(); - public override IDisplayResult Edit(SeoMetaPart part, BuildPartEditorContext context) + var results = new List { - var settings = context.TypePartDefinition.GetSettings(); - - var results = new List + Initialize("SeoMetaPart_Edit", model => { - Initialize("SeoMetaPart_Edit", model => - { - model.PageTitle = part.PageTitle; - model.Render = part.Render; - model.MetaDescription = part.MetaDescription; - model.MetaKeywords = part.MetaKeywords; - model.Canonical = part.Canonical; - model.MetaRobots = part.MetaRobots; - model.CustomMetaTags = JConvert.SerializeObject(part.CustomMetaTags, JOptions.CamelCaseIndented); - model.SeoMetaPart = part; - model.Settings = settings; - }).Location("Parts#SEO;50"), - }; - - if (settings.DisplayOpenGraph) + model.PageTitle = part.PageTitle; + model.Render = part.Render; + model.MetaDescription = part.MetaDescription; + model.MetaKeywords = part.MetaKeywords; + model.Canonical = part.Canonical; + model.MetaRobots = part.MetaRobots; + model.CustomMetaTags = JConvert.SerializeObject(part.CustomMetaTags, JOptions.CamelCaseIndented); + model.SeoMetaPart = part; + model.Settings = settings; + }).Location("Parts#SEO;50"), + }; + + if (settings.DisplayOpenGraph) + { + results.Add(Initialize("SeoMetaPartOpenGraph_Edit", model => { - results.Add(Initialize("SeoMetaPartOpenGraph_Edit", model => - { - model.OpenGraphType = part.OpenGraphType; - model.OpenGraphTitle = part.OpenGraphTitle; - model.OpenGraphDescription = part.OpenGraphDescription; - model.SeoMetaPart = part; - }).Location("Parts#SEO;50%Open Graph;20")); - } + model.OpenGraphType = part.OpenGraphType; + model.OpenGraphTitle = part.OpenGraphTitle; + model.OpenGraphDescription = part.OpenGraphDescription; + model.SeoMetaPart = part; + }).Location("Parts#SEO;50%Open Graph;20")); + } - if (settings.DisplayTwitter) + if (settings.DisplayTwitter) + { + results.Add(Initialize("SeoMetaPartTwitter_Edit", model => { - results.Add(Initialize("SeoMetaPartTwitter_Edit", model => - { - model.TwitterTitle = part.TwitterTitle; - model.TwitterDescription = part.TwitterDescription; - model.TwitterCard = part.TwitterCard; - model.TwitterCreator = part.TwitterCreator; - model.TwitterSite = part.TwitterSite; - model.SeoMetaPart = part; - }).Location("Parts#SEO;50%Twitter;30")); - } + model.TwitterTitle = part.TwitterTitle; + model.TwitterDescription = part.TwitterDescription; + model.TwitterCard = part.TwitterCard; + model.TwitterCreator = part.TwitterCreator; + model.TwitterSite = part.TwitterSite; + model.SeoMetaPart = part; + }).Location("Parts#SEO;50%Twitter;30")); + } - if (settings.DisplayGoogleSchema) + if (settings.DisplayGoogleSchema) + { + results.Add(Initialize("SeoMetaPartGoogleSchema_Edit", model => { - results.Add(Initialize("SeoMetaPartGoogleSchema_Edit", model => - { - model.GoogleSchema = part.GoogleSchema; - model.SeoMetaPart = part; - }).Location("Parts#SEO;50%Google Schema;40")); - } - - return Combine(results); + model.GoogleSchema = part.GoogleSchema; + model.SeoMetaPart = part; + }).Location("Parts#SEO;50%Google Schema;40")); } - public override async Task UpdateAsync(SeoMetaPart part, UpdatePartEditorContext context) - { - var partViewModel = new SeoMetaPartViewModel(); - await context.Updater.TryUpdateModelAsync(partViewModel, Prefix); + return Combine(results); + } - try - { - part.Render = partViewModel.Render; - part.PageTitle = partViewModel.PageTitle; - part.MetaDescription = partViewModel.MetaDescription; - part.MetaKeywords = partViewModel.MetaKeywords; - part.Canonical = partViewModel.Canonical; - part.MetaRobots = partViewModel.MetaRobots; - - part.CustomMetaTags = string.IsNullOrWhiteSpace(partViewModel.CustomMetaTags) - ? [] - : JConvert.DeserializeObject(partViewModel.CustomMetaTags); - - if (part.Canonical?.IndexOfAny(SeoMetaPart.InvalidCharactersForCanoncial) > -1 || part.Canonical?.IndexOf(' ') > -1) - { - context.Updater.ModelState.AddModelError(Prefix, S["The canonical entry contains invalid characters."]); - } - } - catch + public override async Task UpdateAsync(SeoMetaPart part, UpdatePartEditorContext context) + { + var partViewModel = new SeoMetaPartViewModel(); + await context.Updater.TryUpdateModelAsync(partViewModel, Prefix); + + try + { + part.Render = partViewModel.Render; + part.PageTitle = partViewModel.PageTitle; + part.MetaDescription = partViewModel.MetaDescription; + part.MetaKeywords = partViewModel.MetaKeywords; + part.Canonical = partViewModel.Canonical; + part.MetaRobots = partViewModel.MetaRobots; + + part.CustomMetaTags = string.IsNullOrWhiteSpace(partViewModel.CustomMetaTags) + ? [] + : JConvert.DeserializeObject(partViewModel.CustomMetaTags); + + if (part.Canonical?.IndexOfAny(SeoMetaPart.InvalidCharactersForCanoncial) > -1 || part.Canonical?.IndexOf(' ') > -1) { - context.Updater.ModelState.AddModelError(Prefix, S["The meta entries are written in an incorrect format."]); + context.Updater.ModelState.AddModelError(Prefix, S["The canonical entry contains invalid characters."]); } + } + catch + { + context.Updater.ModelState.AddModelError(Prefix, S["The meta entries are written in an incorrect format."]); + } - var openGraphModel = new SeoMetaPartOpenGraphViewModel(); - - await context.Updater.TryUpdateModelAsync(openGraphModel, Prefix); + var openGraphModel = new SeoMetaPartOpenGraphViewModel(); - part.OpenGraphType = openGraphModel.OpenGraphType; - part.OpenGraphTitle = openGraphModel.OpenGraphTitle; - part.OpenGraphDescription = openGraphModel.OpenGraphDescription; + await context.Updater.TryUpdateModelAsync(openGraphModel, Prefix); - var twitterModel = new SeoMetaPartTwitterViewModel(); + part.OpenGraphType = openGraphModel.OpenGraphType; + part.OpenGraphTitle = openGraphModel.OpenGraphTitle; + part.OpenGraphDescription = openGraphModel.OpenGraphDescription; - await context.Updater.TryUpdateModelAsync(twitterModel, Prefix); + var twitterModel = new SeoMetaPartTwitterViewModel(); - part.TwitterTitle = twitterModel.TwitterTitle; - part.TwitterDescription = twitterModel.TwitterDescription; - part.TwitterCard = twitterModel.TwitterCard; - part.TwitterCreator = twitterModel.TwitterCreator; - part.TwitterSite = twitterModel.TwitterSite; + await context.Updater.TryUpdateModelAsync(twitterModel, Prefix); - var googleSchemaModel = new SeoMetaPartGoogleSchemaViewModel(); + part.TwitterTitle = twitterModel.TwitterTitle; + part.TwitterDescription = twitterModel.TwitterDescription; + part.TwitterCard = twitterModel.TwitterCard; + part.TwitterCreator = twitterModel.TwitterCreator; + part.TwitterSite = twitterModel.TwitterSite; - await context.Updater.TryUpdateModelAsync(googleSchemaModel, Prefix); + var googleSchemaModel = new SeoMetaPartGoogleSchemaViewModel(); - part.GoogleSchema = googleSchemaModel.GoogleSchema; - if (!string.IsNullOrWhiteSpace(googleSchemaModel.GoogleSchema) && !googleSchemaModel.GoogleSchema.IsJson()) - { - context.Updater.ModelState.AddModelError(Prefix, S["The google schema is written in an incorrect format."]); - } + await context.Updater.TryUpdateModelAsync(googleSchemaModel, Prefix); - return Edit(part, context); + part.GoogleSchema = googleSchemaModel.GoogleSchema; + if (!string.IsNullOrWhiteSpace(googleSchemaModel.GoogleSchema) && !googleSchemaModel.GoogleSchema.IsJson()) + { + context.Updater.ModelState.AddModelError(Prefix, S["The google schema is written in an incorrect format."]); } + + return Edit(part, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaPartHandler.cs index 2854c4aa8e9..ce5be865cad 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaPartHandler.cs @@ -14,189 +14,188 @@ using OrchardCore.Seo.Models; using OrchardCore.Settings; -namespace OrchardCore.Seo.Drivers +namespace OrchardCore.Seo.Drivers; + +public class SeoMetaPartHandler : ContentPartHandler { - public class SeoMetaPartHandler : ContentPartHandler + private readonly IMediaFileStore _mediaFileStore; + private readonly ISiteService _siteService; + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUrlHelperFactory _urlHelperFactory; + private readonly IContentManager _contentManager; + + public SeoMetaPartHandler( + IMediaFileStore mediaFileStore, + ISiteService siteService, + IActionContextAccessor actionContextAccessor, + IHttpContextAccessor httpContextAccessor, + IUrlHelperFactory urlHelperFactory, + IContentManager contentManager + ) { - private readonly IMediaFileStore _mediaFileStore; - private readonly ISiteService _siteService; - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IUrlHelperFactory _urlHelperFactory; - private readonly IContentManager _contentManager; - - public SeoMetaPartHandler( - IMediaFileStore mediaFileStore, - ISiteService siteService, - IActionContextAccessor actionContextAccessor, - IHttpContextAccessor httpContextAccessor, - IUrlHelperFactory urlHelperFactory, - IContentManager contentManager - ) - { - _mediaFileStore = mediaFileStore; - _siteService = siteService; - _actionContextAccessor = actionContextAccessor; - _httpContextAccessor = httpContextAccessor; - _urlHelperFactory = urlHelperFactory; - _contentManager = contentManager; - } + _mediaFileStore = mediaFileStore; + _siteService = siteService; + _actionContextAccessor = actionContextAccessor; + _httpContextAccessor = httpContextAccessor; + _urlHelperFactory = urlHelperFactory; + _contentManager = contentManager; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, SeoMetaPart part) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, SeoMetaPart part) + { + return context.ForAsync(async aspect => { - return context.ForAsync(async aspect => - { - aspect.Render = part.Render; - - if (!string.IsNullOrEmpty(part.PageTitle)) - { - aspect.PageTitle = part.PageTitle; - } - - if (!string.IsNullOrEmpty(part.MetaDescription)) - { - aspect.MetaDescription = part.MetaDescription; - } - - if (!string.IsNullOrEmpty(part.MetaKeywords)) - { - aspect.MetaKeywords = part.MetaKeywords; - } - - if (!string.IsNullOrEmpty(part.MetaRobots)) - { - aspect.MetaRobots = part.MetaRobots; - } - - aspect.CustomMetaTags = part.CustomMetaTags; - - var siteSettings = await _siteService.GetSiteSettingsAsync(); - - var actionContext = _actionContextAccessor.ActionContext; - - actionContext ??= await GetActionContextAsync(_httpContextAccessor.HttpContext); - - var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext); - - if (!string.IsNullOrEmpty(part.Canonical)) - { - aspect.Canonical = part.Canonical; - } - else - { - var contentItemMetadata = await _contentManager.PopulateAspectAsync(part.ContentItem); - var relativeUrl = urlHelper.RouteUrl(contentItemMetadata.DisplayRouteValues); - aspect.Canonical = urlHelper.ToAbsoluteUrl(relativeUrl); - } - - // OpenGraph - if (part.OpenGraphImage?.Paths?.Length > 0) - { - aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.OpenGraphImage.Paths[0])); - } - else if (part.DefaultSocialImage?.Paths?.Length > 0) - { - aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.DefaultSocialImage.Paths[0])); - } - - if (part.OpenGraphImage?.MediaTexts?.Length > 0) - { - aspect.OpenGraphImageAlt = part.OpenGraphImage.MediaTexts[0]; - } - else if (part.DefaultSocialImage?.MediaTexts?.Length > 0) - { - aspect.OpenGraphImageAlt = part.DefaultSocialImage.MediaTexts[0]; - } - - if (!string.IsNullOrEmpty(part.OpenGraphTitle)) - { - aspect.OpenGraphTitle = part.OpenGraphTitle; - } - else - { - aspect.OpenGraphTitle = part.PageTitle; - } - - if (!string.IsNullOrEmpty(part.OpenGraphDescription)) - { - aspect.OpenGraphDescription = part.OpenGraphDescription; - } - else - { - aspect.OpenGraphDescription = part.MetaDescription; - } - - // Twitter - - if (part.TwitterImage?.Paths?.Length > 0) - { - aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.TwitterImage.Paths[0])); - } - else if (part.DefaultSocialImage?.Paths?.Length > 0) - { - aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.DefaultSocialImage.Paths[0])); - } - - if (part.TwitterImage?.MediaTexts?.Length > 0) - { - aspect.TwitterImageAlt = part.TwitterImage.MediaTexts[0]; - } - else if (part.DefaultSocialImage?.MediaTexts?.Length > 0) - { - aspect.TwitterImageAlt = part.DefaultSocialImage.MediaTexts[0]; - } - - if (!string.IsNullOrEmpty(part.TwitterTitle)) - { - aspect.TwitterTitle = part.TwitterTitle; - } - else - { - aspect.TwitterTitle = part.PageTitle; - } - - if (!string.IsNullOrEmpty(part.TwitterDescription)) - { - aspect.TwitterDescription = part.TwitterDescription; - } - else - { - aspect.TwitterDescription = part.MetaDescription; - } - - if (!string.IsNullOrEmpty(part.TwitterCard)) - { - aspect.TwitterCard = part.TwitterCard; - } - - if (!string.IsNullOrEmpty(part.TwitterCreator)) - { - aspect.TwitterCreator = part.TwitterCreator; - } - - if (!string.IsNullOrEmpty(part.TwitterSite)) - { - aspect.TwitterSite = part.TwitterSite; - } - - aspect.GoogleSchema = part.GoogleSchema; - }); - } + aspect.Render = part.Render; - internal static async Task GetActionContextAsync(HttpContext httpContext) - { - var routeData = new RouteData(); - routeData.Routers.Add(new RouteCollection()); + if (!string.IsNullOrEmpty(part.PageTitle)) + { + aspect.PageTitle = part.PageTitle; + } - var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); - var filters = httpContext.RequestServices.GetServices(); + if (!string.IsNullOrEmpty(part.MetaDescription)) + { + aspect.MetaDescription = part.MetaDescription; + } - foreach (var filter in filters) + if (!string.IsNullOrEmpty(part.MetaKeywords)) { - await filter.OnActionExecutionAsync(actionContext); + aspect.MetaKeywords = part.MetaKeywords; } - return actionContext; + if (!string.IsNullOrEmpty(part.MetaRobots)) + { + aspect.MetaRobots = part.MetaRobots; + } + + aspect.CustomMetaTags = part.CustomMetaTags; + + var siteSettings = await _siteService.GetSiteSettingsAsync(); + + var actionContext = _actionContextAccessor.ActionContext; + + actionContext ??= await GetActionContextAsync(_httpContextAccessor.HttpContext); + + var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext); + + if (!string.IsNullOrEmpty(part.Canonical)) + { + aspect.Canonical = part.Canonical; + } + else + { + var contentItemMetadata = await _contentManager.PopulateAspectAsync(part.ContentItem); + var relativeUrl = urlHelper.RouteUrl(contentItemMetadata.DisplayRouteValues); + aspect.Canonical = urlHelper.ToAbsoluteUrl(relativeUrl); + } + + // OpenGraph + if (part.OpenGraphImage?.Paths?.Length > 0) + { + aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.OpenGraphImage.Paths[0])); + } + else if (part.DefaultSocialImage?.Paths?.Length > 0) + { + aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.DefaultSocialImage.Paths[0])); + } + + if (part.OpenGraphImage?.MediaTexts?.Length > 0) + { + aspect.OpenGraphImageAlt = part.OpenGraphImage.MediaTexts[0]; + } + else if (part.DefaultSocialImage?.MediaTexts?.Length > 0) + { + aspect.OpenGraphImageAlt = part.DefaultSocialImage.MediaTexts[0]; + } + + if (!string.IsNullOrEmpty(part.OpenGraphTitle)) + { + aspect.OpenGraphTitle = part.OpenGraphTitle; + } + else + { + aspect.OpenGraphTitle = part.PageTitle; + } + + if (!string.IsNullOrEmpty(part.OpenGraphDescription)) + { + aspect.OpenGraphDescription = part.OpenGraphDescription; + } + else + { + aspect.OpenGraphDescription = part.MetaDescription; + } + + // Twitter + + if (part.TwitterImage?.Paths?.Length > 0) + { + aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.TwitterImage.Paths[0])); + } + else if (part.DefaultSocialImage?.Paths?.Length > 0) + { + aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(part.DefaultSocialImage.Paths[0])); + } + + if (part.TwitterImage?.MediaTexts?.Length > 0) + { + aspect.TwitterImageAlt = part.TwitterImage.MediaTexts[0]; + } + else if (part.DefaultSocialImage?.MediaTexts?.Length > 0) + { + aspect.TwitterImageAlt = part.DefaultSocialImage.MediaTexts[0]; + } + + if (!string.IsNullOrEmpty(part.TwitterTitle)) + { + aspect.TwitterTitle = part.TwitterTitle; + } + else + { + aspect.TwitterTitle = part.PageTitle; + } + + if (!string.IsNullOrEmpty(part.TwitterDescription)) + { + aspect.TwitterDescription = part.TwitterDescription; + } + else + { + aspect.TwitterDescription = part.MetaDescription; + } + + if (!string.IsNullOrEmpty(part.TwitterCard)) + { + aspect.TwitterCard = part.TwitterCard; + } + + if (!string.IsNullOrEmpty(part.TwitterCreator)) + { + aspect.TwitterCreator = part.TwitterCreator; + } + + if (!string.IsNullOrEmpty(part.TwitterSite)) + { + aspect.TwitterSite = part.TwitterSite; + } + + aspect.GoogleSchema = part.GoogleSchema; + }); + } + + internal static async Task GetActionContextAsync(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(new RouteCollection()); + + var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); + var filters = httpContext.RequestServices.GetServices(); + + foreach (var filter in filters) + { + await filter.OnActionExecutionAsync(actionContext); } + + return actionContext; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaSettingsHandler.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaSettingsHandler.cs index d9f4aed055d..7628af52a2c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaSettingsHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Handlers/SeoMetaSettingsHandler.cs @@ -15,211 +15,210 @@ using OrchardCore.Seo.Models; using OrchardCore.Settings; -namespace OrchardCore.Seo.Drivers +namespace OrchardCore.Seo.Drivers; + +public class SeoMetaSettingsHandler : ContentHandlerBase { - public class SeoMetaSettingsHandler : ContentHandlerBase + private readonly IMediaFileStore _mediaFileStore; + private readonly ISiteService _siteService; + private readonly IActionContextAccessor _actionContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IUrlHelperFactory _urlHelperFactory; + private IContentManager _contentManager; + + public SeoMetaSettingsHandler( + IMediaFileStore mediaFileStore, + ISiteService siteService, + IActionContextAccessor actionContextAccessor, + IHttpContextAccessor httpContextAccessor, + IUrlHelperFactory urlHelperFactory + ) { - private readonly IMediaFileStore _mediaFileStore; - private readonly ISiteService _siteService; - private readonly IActionContextAccessor _actionContextAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IUrlHelperFactory _urlHelperFactory; - private IContentManager _contentManager; - - public SeoMetaSettingsHandler( - IMediaFileStore mediaFileStore, - ISiteService siteService, - IActionContextAccessor actionContextAccessor, - IHttpContextAccessor httpContextAccessor, - IUrlHelperFactory urlHelperFactory - ) - { - _mediaFileStore = mediaFileStore; - _siteService = siteService; - _actionContextAccessor = actionContextAccessor; - _httpContextAccessor = httpContextAccessor; - _urlHelperFactory = urlHelperFactory; - } + _mediaFileStore = mediaFileStore; + _siteService = siteService; + _actionContextAccessor = actionContextAccessor; + _httpContextAccessor = httpContextAccessor; + _urlHelperFactory = urlHelperFactory; + } - public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + { + return context.ForAsync(async aspect => { - return context.ForAsync(async aspect => - { - // This handlers provides defaults, either from the Seo Meta Settings, or ensures values by default. (title etc) - _contentManager ??= _httpContextAccessor.HttpContext.RequestServices.GetRequiredService(); - var siteSettings = await _siteService.GetSiteSettingsAsync(); - var metaSettings = siteSettings.As("SocialMetaSettings"); + // This handlers provides defaults, either from the Seo Meta Settings, or ensures values by default. (title etc) + _contentManager ??= _httpContextAccessor.HttpContext.RequestServices.GetRequiredService(); + var siteSettings = await _siteService.GetSiteSettingsAsync(); + var metaSettings = siteSettings.As("SocialMetaSettings"); - var actionContext = _actionContextAccessor.ActionContext; + var actionContext = _actionContextAccessor.ActionContext; - actionContext ??= await GetActionContextAsync(_httpContextAccessor.HttpContext); + actionContext ??= await GetActionContextAsync(_httpContextAccessor.HttpContext); - var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext); - var contentItemMetadata = await _contentManager.PopulateAspectAsync(context.ContentItem); + var urlHelper = _urlHelperFactory.GetUrlHelper(actionContext); + var contentItemMetadata = await _contentManager.PopulateAspectAsync(context.ContentItem); - var relativeUrl = urlHelper.RouteUrl(contentItemMetadata.DisplayRouteValues); - var absoluteUrl = urlHelper.ToAbsoluteUrl(relativeUrl); + var relativeUrl = urlHelper.RouteUrl(contentItemMetadata.DisplayRouteValues); + var absoluteUrl = urlHelper.ToAbsoluteUrl(relativeUrl); - // Logic is this happens last after the part settings. - // so if values are not set it is responsible for settings them. + // Logic is this happens last after the part settings. + // so if values are not set it is responsible for settings them. - string defaultImage = metaSettings.Content.SocialMetaSettings?.DefaultSocialImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings.DefaultSocialImage.Paths[0] : string.Empty; - string openGraphImage = metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.Paths[0] : string.Empty; - string twitterImage = metaSettings.Content.SocialMetaSettings?.TwitterImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.TwitterImage?.Paths[0] : string.Empty; + string defaultImage = metaSettings.Content.SocialMetaSettings?.DefaultSocialImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings.DefaultSocialImage.Paths[0] : string.Empty; + string openGraphImage = metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.Paths[0] : string.Empty; + string twitterImage = metaSettings.Content.SocialMetaSettings?.TwitterImage?.Paths?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.TwitterImage?.Paths[0] : string.Empty; - string defaultAltText = metaSettings.Content.SocialMetaSettings?.DefaultSocialImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings.DefaultSocialImage.MediaTexts[0] : string.Empty; - string openGraphAltText = metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.MediaTexts[0] : string.Empty; - string twitterAltText = metaSettings.Content.SocialMetaSettings?.TwitterImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.TwitterImage?.MediaTexts[0] : string.Empty; + string defaultAltText = metaSettings.Content.SocialMetaSettings?.DefaultSocialImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings.DefaultSocialImage.MediaTexts[0] : string.Empty; + string openGraphAltText = metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.OpenGraphImage?.MediaTexts[0] : string.Empty; + string twitterAltText = metaSettings.Content.SocialMetaSettings?.TwitterImage?.MediaTexts?.Count > 0 ? metaSettings.Content.SocialMetaSettings?.TwitterImage?.MediaTexts[0] : string.Empty; - string twitterCard = metaSettings.Content.SocialMetaSettings?.TwitterCard?.Text?.ToString(); - string twitterCreator = metaSettings.Content.SocialMetaSettings?.TwitterCreator?.Text?.ToString(); - string twitterSite = metaSettings.Content.SocialMetaSettings?.TwitterSite?.Text?.ToString(); + string twitterCard = metaSettings.Content.SocialMetaSettings?.TwitterCard?.Text?.ToString(); + string twitterCreator = metaSettings.Content.SocialMetaSettings?.TwitterCreator?.Text?.ToString(); + string twitterSite = metaSettings.Content.SocialMetaSettings?.TwitterSite?.Text?.ToString(); - string googleSchema = metaSettings.Content.SocialMetaSettings?.GoogleSchema?.Text?.ToString(); + string googleSchema = metaSettings.Content.SocialMetaSettings?.GoogleSchema?.Text?.ToString(); - // Meta + // Meta - if (string.IsNullOrEmpty(aspect.MetaDescription)) - { - aspect.MetaDescription = metaSettings.Content.SocialMetaSettings?.DefaultMetaDescription?.Text?.ToString(); - } + if (string.IsNullOrEmpty(aspect.MetaDescription)) + { + aspect.MetaDescription = metaSettings.Content.SocialMetaSettings?.DefaultMetaDescription?.Text?.ToString(); + } - // OpenGraph + // OpenGraph - aspect.OpenGraphUrl = aspect.Canonical ??= absoluteUrl; + aspect.OpenGraphUrl = aspect.Canonical ??= absoluteUrl; - if (string.IsNullOrEmpty(aspect.OpenGraphType)) - { - aspect.OpenGraphType = metaSettings.Content.SocialMetaSettings?.OpenGraphType?.Text?.ToString(); - } + if (string.IsNullOrEmpty(aspect.OpenGraphType)) + { + aspect.OpenGraphType = metaSettings.Content.SocialMetaSettings?.OpenGraphType?.Text?.ToString(); + } - if (string.IsNullOrEmpty(aspect.OpenGraphTitle)) - { - aspect.OpenGraphTitle = context.ContentItem.DisplayText; - } + if (string.IsNullOrEmpty(aspect.OpenGraphTitle)) + { + aspect.OpenGraphTitle = context.ContentItem.DisplayText; + } - if (string.IsNullOrEmpty(aspect.OpenGraphDescription)) - { - aspect.OpenGraphDescription = metaSettings.Content.SocialMetaSettings?.DefaultOpenGraphDescription?.Text?.ToString(); - } + if (string.IsNullOrEmpty(aspect.OpenGraphDescription)) + { + aspect.OpenGraphDescription = metaSettings.Content.SocialMetaSettings?.DefaultOpenGraphDescription?.Text?.ToString(); + } + if (string.IsNullOrEmpty(aspect.OpenGraphSiteName)) + { + aspect.OpenGraphSiteName = metaSettings.Content.SocialMetaSettings?.OpenGraphSiteName?.Text?.ToString(); if (string.IsNullOrEmpty(aspect.OpenGraphSiteName)) { - aspect.OpenGraphSiteName = metaSettings.Content.SocialMetaSettings?.OpenGraphSiteName?.Text?.ToString(); - if (string.IsNullOrEmpty(aspect.OpenGraphSiteName)) - { - aspect.OpenGraphSiteName = siteSettings.SiteName; - } + aspect.OpenGraphSiteName = siteSettings.SiteName; } + } - if (string.IsNullOrEmpty(aspect.OpenGraphAppId)) - { - aspect.OpenGraphAppId = metaSettings.Content.SocialMetaSettings?.OpenGraphAppId?.Text?.ToString(); - } + if (string.IsNullOrEmpty(aspect.OpenGraphAppId)) + { + aspect.OpenGraphAppId = metaSettings.Content.SocialMetaSettings?.OpenGraphAppId?.Text?.ToString(); + } - if (string.IsNullOrEmpty(aspect.OpenGraphImage)) + if (string.IsNullOrEmpty(aspect.OpenGraphImage)) + { + if (string.IsNullOrEmpty(openGraphImage)) { - if (string.IsNullOrEmpty(openGraphImage)) - { - if (!string.IsNullOrEmpty(defaultImage)) - { - aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(defaultImage)); - } - } - else + if (!string.IsNullOrEmpty(defaultImage)) { - aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(openGraphImage)); + aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(defaultImage)); } } - - if (string.IsNullOrEmpty(aspect.OpenGraphImageAlt)) + else { - if (string.IsNullOrEmpty(openGraphAltText)) - { - aspect.OpenGraphImageAlt = defaultAltText; - } - else - { - aspect.OpenGraphImageAlt = openGraphAltText; - } + aspect.OpenGraphImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(openGraphImage)); } + } - // Twitter - aspect.TwitterUrl = aspect.Canonical ??= absoluteUrl; - - if (string.IsNullOrEmpty(aspect.TwitterTitle)) + if (string.IsNullOrEmpty(aspect.OpenGraphImageAlt)) + { + if (string.IsNullOrEmpty(openGraphAltText)) { - aspect.TwitterTitle = context.ContentItem.DisplayText; + aspect.OpenGraphImageAlt = defaultAltText; } - - if (string.IsNullOrEmpty(aspect.TwitterDescription)) + else { - aspect.TwitterDescription = metaSettings.Content.SocialMetaSettings?.DefaultTwitterDescription?.Text?.ToString(); + aspect.OpenGraphImageAlt = openGraphAltText; } + } - if (string.IsNullOrEmpty(aspect.TwitterCard)) - { - aspect.TwitterCard = twitterCard; - } + // Twitter + aspect.TwitterUrl = aspect.Canonical ??= absoluteUrl; - if (string.IsNullOrEmpty(aspect.TwitterSite)) - { - aspect.TwitterSite = twitterSite; - } + if (string.IsNullOrEmpty(aspect.TwitterTitle)) + { + aspect.TwitterTitle = context.ContentItem.DisplayText; + } - if (string.IsNullOrEmpty(aspect.TwitterCreator)) - { - aspect.TwitterCreator = twitterCreator; - } + if (string.IsNullOrEmpty(aspect.TwitterDescription)) + { + aspect.TwitterDescription = metaSettings.Content.SocialMetaSettings?.DefaultTwitterDescription?.Text?.ToString(); + } - if (string.IsNullOrEmpty(aspect.TwitterImage)) + if (string.IsNullOrEmpty(aspect.TwitterCard)) + { + aspect.TwitterCard = twitterCard; + } + + if (string.IsNullOrEmpty(aspect.TwitterSite)) + { + aspect.TwitterSite = twitterSite; + } + + if (string.IsNullOrEmpty(aspect.TwitterCreator)) + { + aspect.TwitterCreator = twitterCreator; + } + + if (string.IsNullOrEmpty(aspect.TwitterImage)) + { + if (string.IsNullOrEmpty(twitterImage)) { - if (string.IsNullOrEmpty(twitterImage)) - { - if (!string.IsNullOrEmpty(defaultImage)) - { - aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(defaultImage)); - } - } - else + if (!string.IsNullOrEmpty(defaultImage)) { - aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(twitterImage)); + aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(defaultImage)); } } - - if (string.IsNullOrEmpty(aspect.TwitterImageAlt)) + else { - if (string.IsNullOrEmpty(twitterAltText)) - { - aspect.TwitterImageAlt = defaultAltText; - } - else - { - aspect.TwitterImageAlt = twitterAltText; - } + aspect.TwitterImage = urlHelper.ToAbsoluteUrl(_mediaFileStore.MapPathToPublicUrl(twitterImage)); } + } - if (string.IsNullOrEmpty(aspect.GoogleSchema)) + if (string.IsNullOrEmpty(aspect.TwitterImageAlt)) + { + if (string.IsNullOrEmpty(twitterAltText)) { - aspect.GoogleSchema = googleSchema; + aspect.TwitterImageAlt = defaultAltText; } - }); - } - - internal static async Task GetActionContextAsync(HttpContext httpContext) - { - var routeData = new RouteData(); - routeData.Routers.Add(new RouteCollection()); - - var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); - var filters = httpContext.RequestServices.GetServices(); + else + { + aspect.TwitterImageAlt = twitterAltText; + } + } - foreach (var filter in filters) + if (string.IsNullOrEmpty(aspect.GoogleSchema)) { - await filter.OnActionExecutionAsync(actionContext); + aspect.GoogleSchema = googleSchema; } + }); + } - return actionContext; + internal static async Task GetActionContextAsync(HttpContext httpContext) + { + var routeData = new RouteData(); + routeData.Routers.Add(new RouteCollection()); + + var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); + var filters = httpContext.RequestServices.GetServices(); + + foreach (var filter in filters) + { + await filter.OnActionExecutionAsync(actionContext); } + + return actionContext; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Migrations.cs index a9f4be20eb5..7c7d1d5159b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Migrations.cs @@ -6,48 +6,47 @@ using OrchardCore.Recipes; using OrchardCore.Recipes.Services; -namespace OrchardCore.Seo +namespace OrchardCore.Seo; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IRecipeMigrator _recipeMigrator; + + public Migrations(IContentDefinitionManager contentDefinitionManager, IRecipeMigrator recipeMigrator) + { + _contentDefinitionManager = contentDefinitionManager; + _recipeMigrator = recipeMigrator; + } + + public async Task CreateAsync() { - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IRecipeMigrator _recipeMigrator; - - public Migrations(IContentDefinitionManager contentDefinitionManager, IRecipeMigrator recipeMigrator) - { - _contentDefinitionManager = contentDefinitionManager; - _recipeMigrator = recipeMigrator; - } - - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("SeoMetaPart", builder => builder - .Attachable() - .WithDescription("Provides a part that allows SEO meta descriptions to be applied to a content item.") - .WithField("DefaultSocialImage", field => field - .OfType("MediaField") - .WithDisplayName("Default social image") - .WithSettings(new MediaFieldSettings { Multiple = false })) - .WithField("OpenGraphImage", field => field - .OfType("MediaField") - .WithDisplayName("Open graph image") - .WithSettings(new MediaFieldSettings { Multiple = false })) - .WithField("TwitterImage", field => field - .OfType("MediaField") - .WithDisplayName("Twitter image") - .WithSettings(new MediaFieldSettings { Multiple = false })) - ); - - await _recipeMigrator.ExecuteAsync("socialmetasettings.recipe.json", this); - - return 2; - } - - public async Task UpdateFrom1Async() - { - await _recipeMigrator.ExecuteAsync($"socialmetasettings{RecipesConstants.RecipeExtension}", this); - - return 2; - } + await _contentDefinitionManager.AlterPartDefinitionAsync("SeoMetaPart", builder => builder + .Attachable() + .WithDescription("Provides a part that allows SEO meta descriptions to be applied to a content item.") + .WithField("DefaultSocialImage", field => field + .OfType("MediaField") + .WithDisplayName("Default social image") + .WithSettings(new MediaFieldSettings { Multiple = false })) + .WithField("OpenGraphImage", field => field + .OfType("MediaField") + .WithDisplayName("Open graph image") + .WithSettings(new MediaFieldSettings { Multiple = false })) + .WithField("TwitterImage", field => field + .OfType("MediaField") + .WithDisplayName("Twitter image") + .WithSettings(new MediaFieldSettings { Multiple = false })) + ); + + await _recipeMigrator.ExecuteAsync("socialmetasettings.recipe.json", this); + + return 2; + } + + public async Task UpdateFrom1Async() + { + await _recipeMigrator.ExecuteAsync($"socialmetasettings{RecipesConstants.RecipeExtension}", this); + + return 2; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoAspect.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoAspect.cs index e6814f3ae15..369525c8761 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoAspect.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoAspect.cs @@ -1,43 +1,42 @@ using System.Globalization; using OrchardCore.ResourceManagement; -namespace OrchardCore.Seo.Models +namespace OrchardCore.Seo.Models; + +public class SeoAspect { - public class SeoAspect - { - public string PageTitle { get; set; } - public bool Render { get; set; } = true; - - public string MetaDescription { get; set; } - - public string MetaKeywords { get; set; } - public string Canonical { get; set; } - - public string MetaRobots { get; set; } - public MetaEntry[] CustomMetaTags { get; set; } = []; - - // Twitter card - public string TwitterCard { get; set; } - public string TwitterSite { get; set; } // comes from settings. - public string TwitterCreator { get; set; } - public string TwitterTitle { get; set; } // comes from page title. - public string TwitterDescription { get; set; } // comes from MetaDescription - public string TwitterUrl { get; set; } - - public string TwitterImage { get; set; } - public string TwitterImageAlt { get; set; } - - // OpenGraph - public string OpenGraphType { get; set; } - public string OpenGraphTitle { get; set; } // comes from page title - public string OpenGraphSiteName { get; set; } - public string OpenGraphDescription { get; set; } // comes from MetaDescription - public string OpenGraphUrl { get; set; } - public string OpenGraphImage { get; set; } - public string OpenGraphImageAlt { get; set; } - public string OpenGraphLocale { get; set; } = CultureInfo.CurrentUICulture.Name; - public string OpenGraphAppId { get; set; } - - public string GoogleSchema { get; set; } - } + public string PageTitle { get; set; } + public bool Render { get; set; } = true; + + public string MetaDescription { get; set; } + + public string MetaKeywords { get; set; } + public string Canonical { get; set; } + + public string MetaRobots { get; set; } + public MetaEntry[] CustomMetaTags { get; set; } = []; + + // Twitter card + public string TwitterCard { get; set; } + public string TwitterSite { get; set; } // comes from settings. + public string TwitterCreator { get; set; } + public string TwitterTitle { get; set; } // comes from page title. + public string TwitterDescription { get; set; } // comes from MetaDescription + public string TwitterUrl { get; set; } + + public string TwitterImage { get; set; } + public string TwitterImageAlt { get; set; } + + // OpenGraph + public string OpenGraphType { get; set; } + public string OpenGraphTitle { get; set; } // comes from page title + public string OpenGraphSiteName { get; set; } + public string OpenGraphDescription { get; set; } // comes from MetaDescription + public string OpenGraphUrl { get; set; } + public string OpenGraphImage { get; set; } + public string OpenGraphImageAlt { get; set; } + public string OpenGraphLocale { get; set; } = CultureInfo.CurrentUICulture.Name; + public string OpenGraphAppId { get; set; } + + public string GoogleSchema { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPart.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPart.cs index aea656f5afe..3b61110318a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPart.cs @@ -3,41 +3,40 @@ using OrchardCore.Media.Fields; using OrchardCore.ResourceManagement; -namespace OrchardCore.Seo.Models +namespace OrchardCore.Seo.Models; + +public class SeoMetaPart : ContentPart { - public class SeoMetaPart : ContentPart - { - public static readonly char[] InvalidCharactersForCanoncial = "?#[]@!$&'()*+,;=<>\\|%".ToCharArray(); - public string PageTitle { get; set; } + public static readonly char[] InvalidCharactersForCanoncial = "?#[]@!$&'()*+,;=<>\\|%".ToCharArray(); + public string PageTitle { get; set; } - [DefaultValue(true)] - public bool Render { get; set; } = true; + [DefaultValue(true)] + public bool Render { get; set; } = true; - public string MetaDescription { get; set; } + public string MetaDescription { get; set; } - public string MetaKeywords { get; set; } + public string MetaKeywords { get; set; } - public string Canonical { get; set; } + public string Canonical { get; set; } - public string MetaRobots { get; set; } - public MetaEntry[] CustomMetaTags { get; set; } = []; + public string MetaRobots { get; set; } + public MetaEntry[] CustomMetaTags { get; set; } = []; - public MediaField DefaultSocialImage { get; set; } + public MediaField DefaultSocialImage { get; set; } - public MediaField OpenGraphImage { get; set; } - public string OpenGraphType { get; set; } - public string OpenGraphTitle { get; set; } - public string OpenGraphDescription { get; set; } + public MediaField OpenGraphImage { get; set; } + public string OpenGraphType { get; set; } + public string OpenGraphTitle { get; set; } + public string OpenGraphDescription { get; set; } - public MediaField TwitterImage { get; set; } - public string TwitterTitle { get; set; } - public string TwitterDescription { get; set; } - public string TwitterCard { get; set; } - public string TwitterCreator { get; set; } - public string TwitterSite { get; set; } + public MediaField TwitterImage { get; set; } + public string TwitterTitle { get; set; } + public string TwitterDescription { get; set; } + public string TwitterCard { get; set; } + public string TwitterCreator { get; set; } + public string TwitterSite { get; set; } - public string GoogleSchema { get; set; } - } + public string GoogleSchema { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPartSettings.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPartSettings.cs index 6141a334e33..c7c90b72ce3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPartSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Models/SeoMetaPartSettings.cs @@ -1,11 +1,10 @@ -namespace OrchardCore.Seo.Models +namespace OrchardCore.Seo.Models; + +public class SeoMetaPartSettings { - public class SeoMetaPartSettings - { - public bool DisplayKeywords { get; set; } - public bool DisplayCustomMetaTags { get; set; } - public bool DisplayOpenGraph { get; set; } - public bool DisplayTwitter { get; set; } - public bool DisplayGoogleSchema { get; set; } - } + public bool DisplayKeywords { get; set; } + public bool DisplayCustomMetaTags { get; set; } + public bool DisplayOpenGraph { get; set; } + public bool DisplayTwitter { get; set; } + public bool DisplayGoogleSchema { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Settings/SeoMetaPartSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Settings/SeoMetaPartSettingsDisplayDriver.cs index f4f41a1c30c..7e547e03633 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Settings/SeoMetaPartSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Settings/SeoMetaPartSettingsDisplayDriver.cs @@ -6,45 +6,44 @@ using OrchardCore.Seo.Models; using OrchardCore.Seo.ViewModels; -namespace OrchardCore.SeoMeta.Settings +namespace OrchardCore.SeoMeta.Settings; + +public sealed class SeoMetaPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver { - public sealed class SeoMetaPartSettingsDisplayDriver : ContentTypePartDefinitionDisplayDriver + public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentTypePartDefinition contentTypePartDefinition, BuildEditorContext context) + return Initialize("SeoMetaPartSettings_Edit", model => { - return Initialize("SeoMetaPartSettings_Edit", model => - { - var settings = contentTypePartDefinition.GetSettings(); + var settings = contentTypePartDefinition.GetSettings(); - model.DisplayKeywords = settings.DisplayKeywords; - model.DisplayCustomMetaTags = settings.DisplayCustomMetaTags; - model.DisplayOpenGraph = settings.DisplayOpenGraph; - model.DisplayTwitter = settings.DisplayTwitter; - model.DisplayGoogleSchema = settings.DisplayGoogleSchema; - }).Location("Content"); - } + model.DisplayKeywords = settings.DisplayKeywords; + model.DisplayCustomMetaTags = settings.DisplayCustomMetaTags; + model.DisplayOpenGraph = settings.DisplayOpenGraph; + model.DisplayTwitter = settings.DisplayTwitter; + model.DisplayGoogleSchema = settings.DisplayGoogleSchema; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) - { - var model = new SeoMetaPartSettingsViewModel(); + public override async Task UpdateAsync(ContentTypePartDefinition contentTypePartDefinition, UpdateTypePartEditorContext context) + { + var model = new SeoMetaPartSettingsViewModel(); - await context.Updater.TryUpdateModelAsync(model, Prefix, - m => m.DisplayKeywords, - m => m.DisplayCustomMetaTags, - m => m.DisplayOpenGraph, - m => m.DisplayTwitter, - m => m.DisplayGoogleSchema); + await context.Updater.TryUpdateModelAsync(model, Prefix, + m => m.DisplayKeywords, + m => m.DisplayCustomMetaTags, + m => m.DisplayOpenGraph, + m => m.DisplayTwitter, + m => m.DisplayGoogleSchema); - context.Builder.WithSettings(new SeoMetaPartSettings - { - DisplayKeywords = model.DisplayKeywords, - DisplayCustomMetaTags = model.DisplayCustomMetaTags, - DisplayOpenGraph = model.DisplayOpenGraph, - DisplayTwitter = model.DisplayTwitter, - DisplayGoogleSchema = model.DisplayGoogleSchema - }); + context.Builder.WithSettings(new SeoMetaPartSettings + { + DisplayKeywords = model.DisplayKeywords, + DisplayCustomMetaTags = model.DisplayCustomMetaTags, + DisplayOpenGraph = model.DisplayOpenGraph, + DisplayTwitter = model.DisplayTwitter, + DisplayGoogleSchema = model.DisplayGoogleSchema + }); - return Edit(contentTypePartDefinition, context); - } + return Edit(contentTypePartDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Seo/Startup.cs index 61af003681d..ddddee917fb 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/Startup.cs @@ -19,34 +19,33 @@ using OrchardCore.SeoMeta.Settings; using OrchardCore.Settings; -namespace OrchardCore.Seo +namespace OrchardCore.Seo; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); + services.AddDataMigration(); - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // This must be last, and the module dependent on Contents so this runs after the part handlers. - services.AddScoped(); - services.AddScoped(); + // This must be last, and the module dependent on Contents so this runs after the part handlers. + services.AddScoped(); + services.AddScoped(); - services.AddScoped(); - services.AddScoped, RobotsSettingsDisplayDriver>(); - services.AddScoped(); - services.AddTransient(); - } + services.AddScoped(); + services.AddScoped, RobotsSettingsDisplayDriver>(); + services.AddScoped(); + services.AddTransient(); + } - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - app.UseMiddleware(); - } + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + app.UseMiddleware(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartGoogleSchemaViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartGoogleSchemaViewModel.cs index dc58d6f93be..607e3495389 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartGoogleSchemaViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartGoogleSchemaViewModel.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Seo.Models; -namespace OrchardCore.Seo.ViewModels +namespace OrchardCore.Seo.ViewModels; + +public class SeoMetaPartGoogleSchemaViewModel { - public class SeoMetaPartGoogleSchemaViewModel - { - public string GoogleSchema { get; set; } + public string GoogleSchema { get; set; } - [BindNever] - public SeoMetaPart SeoMetaPart { get; set; } - } + [BindNever] + public SeoMetaPart SeoMetaPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartOpenGraphViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartOpenGraphViewModel.cs index 57b5c672853..0d463c3da49 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartOpenGraphViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartOpenGraphViewModel.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Seo.Models; -namespace OrchardCore.Seo.ViewModels +namespace OrchardCore.Seo.ViewModels; + +public class SeoMetaPartOpenGraphViewModel { - public class SeoMetaPartOpenGraphViewModel - { - public string OpenGraphType { get; set; } - public string OpenGraphTitle { get; set; } - public string OpenGraphDescription { get; set; } + public string OpenGraphType { get; set; } + public string OpenGraphTitle { get; set; } + public string OpenGraphDescription { get; set; } - [BindNever] - public SeoMetaPart SeoMetaPart { get; set; } - } + [BindNever] + public SeoMetaPart SeoMetaPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartSettingsViewModel.cs index e3600ea22dc..0c116d3f25d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartSettingsViewModel.cs @@ -1,14 +1,13 @@ using OrchardCore.Seo.Models; -namespace OrchardCore.Seo.ViewModels +namespace OrchardCore.Seo.ViewModels; + +public class SeoMetaPartSettingsViewModel { - public class SeoMetaPartSettingsViewModel - { - public bool DisplayKeywords { get; set; } - public bool DisplayCustomMetaTags { get; set; } - public bool DisplayOpenGraph { get; set; } - public bool DisplayTwitter { get; set; } - public bool DisplayGoogleSchema { get; set; } - public SeoMetaPartSettings SeoMetaPartSettings { get; set; } - } + public bool DisplayKeywords { get; set; } + public bool DisplayCustomMetaTags { get; set; } + public bool DisplayOpenGraph { get; set; } + public bool DisplayTwitter { get; set; } + public bool DisplayGoogleSchema { get; set; } + public SeoMetaPartSettings SeoMetaPartSettings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartTwitterViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartTwitterViewModel.cs index a3682c30117..48f56e12940 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartTwitterViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartTwitterViewModel.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Seo.Models; -namespace OrchardCore.Seo.ViewModels +namespace OrchardCore.Seo.ViewModels; + +public class SeoMetaPartTwitterViewModel { - public class SeoMetaPartTwitterViewModel - { - public string TwitterTitle { get; set; } - public string TwitterDescription { get; set; } - public string TwitterCard { get; set; } - public string TwitterCreator { get; set; } - public string TwitterSite { get; set; } + public string TwitterTitle { get; set; } + public string TwitterDescription { get; set; } + public string TwitterCard { get; set; } + public string TwitterCreator { get; set; } + public string TwitterSite { get; set; } - [BindNever] - public SeoMetaPart SeoMetaPart { get; set; } - } + [BindNever] + public SeoMetaPart SeoMetaPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartViewModel.cs index 9ff96f49b89..b54e40c9e9b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Seo/ViewModels/SeoMetaPartViewModel.cs @@ -1,24 +1,23 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Seo.Models; -namespace OrchardCore.Seo.ViewModels +namespace OrchardCore.Seo.ViewModels; + +public class SeoMetaPartViewModel { - public class SeoMetaPartViewModel - { - public string PageTitle { get; set; } - public bool Render { get; set; } + public string PageTitle { get; set; } + public bool Render { get; set; } - public string MetaDescription { get; set; } - public string Canonical { get; set; } + public string MetaDescription { get; set; } + public string Canonical { get; set; } - public string MetaKeywords { get; set; } - public string MetaRobots { get; set; } - public string CustomMetaTags { get; set; } + public string MetaKeywords { get; set; } + public string MetaRobots { get; set; } + public string CustomMetaTags { get; set; } - [BindNever] - public SeoMetaPart SeoMetaPart { get; set; } + [BindNever] + public SeoMetaPart SeoMetaPart { get; set; } - [BindNever] - public SeoMetaPartSettings Settings { get; set; } - } + [BindNever] + public SeoMetaPartSettings Settings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Settings/AdminMenu.cs index 8c881ec1528..5ad263942f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/AdminMenu.cs @@ -4,46 +4,45 @@ using OrchardCore.Navigation; using OrchardCore.Settings.Drivers; -namespace OrchardCore.Settings +namespace OrchardCore.Settings; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", DefaultSiteSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", DefaultSiteSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], NavigationConstants.AdminMenuConfigurationPosition, configuration => configuration - .AddClass("menu-configuration") - .Id("configuration") - .Add(S["Settings"], "1", settings => settings - .Add(S["General"], "1", entry => entry - .AddClass("general") - .Id("general") - .Action("Index", "Admin", _routeValues) - .Permission(Permissions.ManageGroupSettings) - .LocalNav() - ), - priority: 1) - ); - return Task.CompletedTask; } + + builder + .Add(S["Configuration"], NavigationConstants.AdminMenuConfigurationPosition, configuration => configuration + .AddClass("menu-configuration") + .Id("configuration") + .Add(S["Settings"], "1", settings => settings + .Add(S["General"], "1", entry => entry + .AddClass("general") + .Id("general") + .Action("Index", "Admin", _routeValues) + .Permission(Permissions.ManageGroupSettings) + .LocalNav() + ), + priority: 1) + ); + + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Controllers/AdminController.cs index 3b8f5eb850b..14fc4ffbf9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Controllers/AdminController.cs @@ -12,101 +12,100 @@ using OrchardCore.Localization; using OrchardCore.Settings.ViewModels; -namespace OrchardCore.Settings.Controllers +namespace OrchardCore.Settings.Controllers; + +public class AdminController : Controller { - public class AdminController : Controller + private readonly IDisplayManager _siteSettingsDisplayManager; + private readonly IShellReleaseManager _shellReleaseManager; + private readonly ISiteService _siteService; + private readonly INotifier _notifier; + private readonly IAuthorizationService _authorizationService; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly CultureOptions _cultureOptions; + + protected readonly IHtmlLocalizer H; + + public AdminController( + IShellReleaseManager shellReleaseManager, + ISiteService siteService, + IDisplayManager siteSettingsDisplayManager, + IAuthorizationService authorizationService, + INotifier notifier, + IOptions cultureOptions, + IUpdateModelAccessor updateModelAccessor, + IHtmlLocalizer htmlLocalizer) { - private readonly IDisplayManager _siteSettingsDisplayManager; - private readonly IShellReleaseManager _shellReleaseManager; - private readonly ISiteService _siteService; - private readonly INotifier _notifier; - private readonly IAuthorizationService _authorizationService; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly CultureOptions _cultureOptions; - - protected readonly IHtmlLocalizer H; - - public AdminController( - IShellReleaseManager shellReleaseManager, - ISiteService siteService, - IDisplayManager siteSettingsDisplayManager, - IAuthorizationService authorizationService, - INotifier notifier, - IOptions cultureOptions, - IUpdateModelAccessor updateModelAccessor, - IHtmlLocalizer htmlLocalizer) + _siteSettingsDisplayManager = siteSettingsDisplayManager; + _shellReleaseManager = shellReleaseManager; + _siteService = siteService; + _notifier = notifier; + _authorizationService = authorizationService; + _updateModelAccessor = updateModelAccessor; + _cultureOptions = cultureOptions.Value; + H = htmlLocalizer; + } + + [Admin("Settings/{groupId}", "AdminSettings")] + public async Task Index(string groupId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageGroupSettings, (object)groupId)) { - _siteSettingsDisplayManager = siteSettingsDisplayManager; - _shellReleaseManager = shellReleaseManager; - _siteService = siteService; - _notifier = notifier; - _authorizationService = authorizationService; - _updateModelAccessor = updateModelAccessor; - _cultureOptions = cultureOptions.Value; - H = htmlLocalizer; + return Forbid(); } - [Admin("Settings/{groupId}", "AdminSettings")] - public async Task Index(string groupId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageGroupSettings, (object)groupId)) - { - return Forbid(); - } + var site = await _siteService.GetSiteSettingsAsync(); - var site = await _siteService.GetSiteSettingsAsync(); + var viewModel = new AdminIndexViewModel + { + GroupId = groupId, + Shape = await _siteSettingsDisplayManager.BuildEditorAsync(site, _updateModelAccessor.ModelUpdater, false, groupId, string.Empty) + }; - var viewModel = new AdminIndexViewModel - { - GroupId = groupId, - Shape = await _siteSettingsDisplayManager.BuildEditorAsync(site, _updateModelAccessor.ModelUpdater, false, groupId, string.Empty) - }; + return View(viewModel); + } - return View(viewModel); + [HttpPost] + [ActionName(nameof(Index))] + public async Task IndexPost(string groupId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageGroupSettings, (object)groupId)) + { + return Forbid(); } - [HttpPost] - [ActionName(nameof(Index))] - public async Task IndexPost(string groupId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageGroupSettings, (object)groupId)) - { - return Forbid(); - } + var site = await _siteService.LoadSiteSettingsAsync(); - var site = await _siteService.LoadSiteSettingsAsync(); + var viewModel = new AdminIndexViewModel + { + GroupId = groupId, + Shape = await _siteSettingsDisplayManager.UpdateEditorAsync(site, _updateModelAccessor.ModelUpdater, false, groupId, string.Empty) + }; - var viewModel = new AdminIndexViewModel - { - GroupId = groupId, - Shape = await _siteSettingsDisplayManager.UpdateEditorAsync(site, _updateModelAccessor.ModelUpdater, false, groupId, string.Empty) - }; + if (ModelState.IsValid) + { + await _siteService.UpdateSiteSettingsAsync(site); - if (ModelState.IsValid) + string culture = null; + if (site.Properties.TryGetPropertyValue("LocalizationSettings", out var settings)) { - await _siteService.UpdateSiteSettingsAsync(site); - - string culture = null; - if (site.Properties.TryGetPropertyValue("LocalizationSettings", out var settings)) - { - culture = settings.Value("DefaultCulture"); - } - - // We create a transient scope with the newly selected culture to create a notification that will use it instead of the previous culture - using (culture != null ? CultureScope.Create(culture, ignoreSystemSettings: _cultureOptions.IgnoreSystemSettings) : null) - { - await _notifier.SuccessAsync(H["Site settings updated successfully."]); - } - - return RedirectToAction(nameof(Index), new { groupId }); + culture = settings.Value("DefaultCulture"); } - else + + // We create a transient scope with the newly selected culture to create a notification that will use it instead of the previous culture + using (culture != null ? CultureScope.Create(culture, ignoreSystemSettings: _cultureOptions.IgnoreSystemSettings) : null) { - // If the model state is invalid, suspend the request to release the shell so that the tenant is not reloaded. - _shellReleaseManager.SuspendReleaseRequest(); + await _notifier.SuccessAsync(H["Site settings updated successfully."]); } - return View(viewModel); + return RedirectToAction(nameof(Index), new { groupId }); } + else + { + // If the model state is invalid, suspend the request to release the shell so that the tenant is not reloaded. + _shellReleaseManager.SuspendReleaseRequest(); + } + + return View(viewModel); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentSource.cs index 3a163c5705d..1035a9f9e56 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentSource.cs @@ -3,104 +3,103 @@ using System.Threading.Tasks; using OrchardCore.Deployment; -namespace OrchardCore.Settings.Deployment +namespace OrchardCore.Settings.Deployment; + +public class SiteSettingsDeploymentSource : IDeploymentSource { - public class SiteSettingsDeploymentSource : IDeploymentSource + private readonly ISiteService _siteService; + + public SiteSettingsDeploymentSource(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public SiteSettingsDeploymentSource(ISiteService siteService) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not SiteSettingsDeploymentStep settingsStep) { - _siteService = siteService; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + var site = await _siteService.GetSiteSettingsAsync(); + + var data = new JsonObject { ["name"] = "Settings" }; + + foreach (var settingName in settingsStep.Settings) { - if (step is not SiteSettingsDeploymentStep settingsStep) + switch (settingName) { - return; - } + case "BaseUrl": + data.Add(nameof(ISite.BaseUrl), site.BaseUrl); + break; - var site = await _siteService.GetSiteSettingsAsync(); + case "Calendar": + data.Add(nameof(ISite.Calendar), site.Calendar); + break; - var data = new JsonObject { ["name"] = "Settings" }; + case "MaxPagedCount": + data.Add(nameof(ISite.MaxPagedCount), site.MaxPagedCount); + break; - foreach (var settingName in settingsStep.Settings) - { - switch (settingName) - { - case "BaseUrl": - data.Add(nameof(ISite.BaseUrl), site.BaseUrl); - break; - - case "Calendar": - data.Add(nameof(ISite.Calendar), site.Calendar); - break; - - case "MaxPagedCount": - data.Add(nameof(ISite.MaxPagedCount), site.MaxPagedCount); - break; - - case "MaxPageSize": - data.Add(nameof(ISite.MaxPageSize), site.MaxPageSize); - break; - - case "PageSize": - data.Add(nameof(ISite.PageSize), site.PageSize); - break; - - case "ResourceDebugMode": - data.Add(nameof(ISite.ResourceDebugMode), JsonValue.Create(site.ResourceDebugMode)); - break; - - case "SiteName": - data.Add(nameof(ISite.SiteName), site.SiteName); - break; - - case "PageTitleFormat": - data.Add(nameof(ISite.PageTitleFormat), site.PageTitleFormat); - break; - - case "SiteSalt": - data.Add(nameof(ISite.SiteSalt), site.SiteSalt); - break; - - case "SuperUser": - data.Add(nameof(ISite.SuperUser), site.SuperUser); - break; - - case "TimeZoneId": - data.Add(nameof(ISite.TimeZoneId), site.TimeZoneId); - break; - - case "UseCdn": - data.Add(nameof(ISite.UseCdn), site.UseCdn); - break; - - case "CdnBaseUrl": - data.Add(nameof(ISite.CdnBaseUrl), site.CdnBaseUrl); - break; - - case "AppendVersion": - data.Add(nameof(ISite.AppendVersion), site.AppendVersion); - break; - - case "HomeRoute": - data.Add(nameof(ISite.HomeRoute), JObject.FromObject(site.HomeRoute)); - break; - - case "CacheMode": - data.Add(nameof(ISite.CacheMode), JsonValue.Create(site.CacheMode)); - break; - - default: - throw new InvalidOperationException($"Unsupported setting '{settingName}'"); - } - } + case "MaxPageSize": + data.Add(nameof(ISite.MaxPageSize), site.MaxPageSize); + break; - result.Steps.Add(data); + case "PageSize": + data.Add(nameof(ISite.PageSize), site.PageSize); + break; - return; + case "ResourceDebugMode": + data.Add(nameof(ISite.ResourceDebugMode), JsonValue.Create(site.ResourceDebugMode)); + break; + + case "SiteName": + data.Add(nameof(ISite.SiteName), site.SiteName); + break; + + case "PageTitleFormat": + data.Add(nameof(ISite.PageTitleFormat), site.PageTitleFormat); + break; + + case "SiteSalt": + data.Add(nameof(ISite.SiteSalt), site.SiteSalt); + break; + + case "SuperUser": + data.Add(nameof(ISite.SuperUser), site.SuperUser); + break; + + case "TimeZoneId": + data.Add(nameof(ISite.TimeZoneId), site.TimeZoneId); + break; + + case "UseCdn": + data.Add(nameof(ISite.UseCdn), site.UseCdn); + break; + + case "CdnBaseUrl": + data.Add(nameof(ISite.CdnBaseUrl), site.CdnBaseUrl); + break; + + case "AppendVersion": + data.Add(nameof(ISite.AppendVersion), site.AppendVersion); + break; + + case "HomeRoute": + data.Add(nameof(ISite.HomeRoute), JObject.FromObject(site.HomeRoute)); + break; + + case "CacheMode": + data.Add(nameof(ISite.CacheMode), JsonValue.Create(site.CacheMode)); + break; + + default: + throw new InvalidOperationException($"Unsupported setting '{settingName}'"); + } } + + result.Steps.Add(data); + + return; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStep.cs index 79e2a23940e..a307a5cf315 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStep.cs @@ -1,17 +1,16 @@ using OrchardCore.Deployment; -namespace OrchardCore.Settings.Deployment +namespace OrchardCore.Settings.Deployment; + +/// +/// Adds the current site settings to a . +/// +public class SiteSettingsDeploymentStep : DeploymentStep { - /// - /// Adds the current site settings to a . - /// - public class SiteSettingsDeploymentStep : DeploymentStep + public SiteSettingsDeploymentStep() { - public SiteSettingsDeploymentStep() - { - Name = nameof(SiteSettings); - } - - public string[] Settings { get; set; } + Name = nameof(SiteSettings); } + + public string[] Settings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStepDriver.cs index 6371565103a..6cf58183ea6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Deployment/SiteSettingsDeploymentStepDriver.cs @@ -4,35 +4,34 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Settings.ViewModels; -namespace OrchardCore.Settings.Deployment +namespace OrchardCore.Settings.Deployment; + +public sealed class SiteSettingsDeploymentStepDriver : DisplayDriver { - public sealed class SiteSettingsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(SiteSettingsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(SiteSettingsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("SiteSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), - View("SiteSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("SiteSettingsDeploymentStep_Fields_Summary", step).Location("Summary", "Content"), + View("SiteSettingsDeploymentStep_Fields_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(SiteSettingsDeploymentStep step, BuildEditorContext context) + public override IDisplayResult Edit(SiteSettingsDeploymentStep step, BuildEditorContext context) + { + return Initialize("SiteSettingsDeploymentStep_Fields_Edit", model => { - return Initialize("SiteSettingsDeploymentStep_Fields_Edit", model => - { - model.Settings = step.Settings; - }).Location("Content"); - } + model.Settings = step.Settings; + }).Location("Content"); + } - public override async Task UpdateAsync(SiteSettingsDeploymentStep step, UpdateEditorContext context) - { - // Initializes the value to empty otherwise the model is not updated if no type is selected. - step.Settings = []; + public override async Task UpdateAsync(SiteSettingsDeploymentStep step, UpdateEditorContext context) + { + // Initializes the value to empty otherwise the model is not updated if no type is selected. + step.Settings = []; - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.Settings); + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.Settings); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/ButtonsSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/ButtonsSettingsDisplayDriver.cs index e093af251ee..7e3462aca88 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/ButtonsSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/ButtonsSettingsDisplayDriver.cs @@ -2,17 +2,16 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Settings.Drivers +namespace OrchardCore.Settings.Drivers; + +public sealed class ButtonsSettingsDisplayDriver : DisplayDriver { - public sealed class ButtonsSettingsDisplayDriver : DisplayDriver - { - public const string GroupId = "general"; + public const string GroupId = "general"; - public override IDisplayResult Edit(ISite site, BuildEditorContext context) - { - return Dynamic("SiteSettings_SaveButton") - .Location("Actions") - .OnGroup(context.GroupId); // Trick to render the shape for all groups - } + public override IDisplayResult Edit(ISite site, BuildEditorContext context) + { + return Dynamic("SiteSettings_SaveButton") + .Location("Actions") + .OnGroup(context.GroupId); // Trick to render the shape for all groups } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/DefaultSiteSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/DefaultSiteSettingsDisplayDriver.cs index 4f8f4c0c2d7..bbb262b9c00 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/DefaultSiteSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Drivers/DefaultSiteSettingsDisplayDriver.cs @@ -7,105 +7,104 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Settings.ViewModels; -namespace OrchardCore.Settings.Drivers +namespace OrchardCore.Settings.Drivers; + +public sealed class DefaultSiteSettingsDisplayDriver : DisplayDriver { - public sealed class DefaultSiteSettingsDisplayDriver : DisplayDriver + public const string GroupId = "general"; + + private readonly IShellReleaseManager _shellReleaseManager; + + internal readonly IStringLocalizer S; + + public DefaultSiteSettingsDisplayDriver( + IShellReleaseManager shellReleaseManager, + IStringLocalizer stringLocalizer) { - public const string GroupId = "general"; + _shellReleaseManager = shellReleaseManager; + S = stringLocalizer; + } - private readonly IShellReleaseManager _shellReleaseManager; + public override IDisplayResult Edit(ISite site, BuildEditorContext context) + { + if (!IsGeneralGroup(context)) + { + return null; + } - internal readonly IStringLocalizer S; + context.AddTenantReloadWarningWrapper(); + + var result = Combine( + Initialize("Settings_Edit__Site", model => PopulateProperties(site, model)) + .Location("Content:1#Site;10") + .OnGroup(GroupId), + Initialize("Settings_Edit__Resources", model => PopulateProperties(site, model)) + .Location("Content:1#Resources;20") + .OnGroup(GroupId), + Initialize("Settings_Edit__Cache", model => PopulateProperties(site, model)) + .Location("Content:1#Cache;30") + .OnGroup(GroupId) + ); + + return result; + } - public DefaultSiteSettingsDisplayDriver( - IShellReleaseManager shellReleaseManager, - IStringLocalizer stringLocalizer) + public override async Task UpdateAsync(ISite site, UpdateEditorContext context) + { + if (!IsGeneralGroup(context)) { - _shellReleaseManager = shellReleaseManager; - S = stringLocalizer; + return null; } - public override IDisplayResult Edit(ISite site, BuildEditorContext context) + var model = new SiteSettingsViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix); + + site.SiteName = model.SiteName; + site.PageTitleFormat = model.PageTitleFormat; + site.BaseUrl = model.BaseUrl; + site.TimeZoneId = model.TimeZone; + site.PageSize = model.PageSize.Value; + site.UseCdn = model.UseCdn; + site.CdnBaseUrl = model.CdnBaseUrl; + site.ResourceDebugMode = model.ResourceDebugMode; + site.AppendVersion = model.AppendVersion; + site.CacheMode = model.CacheMode; + + if (model.PageSize.Value < 1) { - if (!IsGeneralGroup(context)) - { - return null; - } - - context.AddTenantReloadWarningWrapper(); - - var result = Combine( - Initialize("Settings_Edit__Site", model => PopulateProperties(site, model)) - .Location("Content:1#Site;10") - .OnGroup(GroupId), - Initialize("Settings_Edit__Resources", model => PopulateProperties(site, model)) - .Location("Content:1#Resources;20") - .OnGroup(GroupId), - Initialize("Settings_Edit__Cache", model => PopulateProperties(site, model)) - .Location("Content:1#Cache;30") - .OnGroup(GroupId) - ); - - return result; + context.Updater.ModelState.AddModelError(Prefix, nameof(model.PageSize), S["The page size must be greater than zero."]); } - public override async Task UpdateAsync(ISite site, UpdateEditorContext context) + if (site.MaxPageSize > 0 && model.PageSize.Value > site.MaxPageSize) { - if (!IsGeneralGroup(context)) - { - return null; - } - - var model = new SiteSettingsViewModel(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); - - site.SiteName = model.SiteName; - site.PageTitleFormat = model.PageTitleFormat; - site.BaseUrl = model.BaseUrl; - site.TimeZoneId = model.TimeZone; - site.PageSize = model.PageSize.Value; - site.UseCdn = model.UseCdn; - site.CdnBaseUrl = model.CdnBaseUrl; - site.ResourceDebugMode = model.ResourceDebugMode; - site.AppendVersion = model.AppendVersion; - site.CacheMode = model.CacheMode; - - if (model.PageSize.Value < 1) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.PageSize), S["The page size must be greater than zero."]); - } - - if (site.MaxPageSize > 0 && model.PageSize.Value > site.MaxPageSize) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.PageSize), S["The page size must be less than or equal to {0}.", site.MaxPageSize]); - } - - if (!string.IsNullOrEmpty(site.BaseUrl) && !Uri.TryCreate(site.BaseUrl, UriKind.Absolute, out _)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.BaseUrl), S["The Base url must be a fully qualified URL."]); - } - - _shellReleaseManager.RequestRelease(); - - return await EditAsync(site, context); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.PageSize), S["The page size must be less than or equal to {0}.", site.MaxPageSize]); } - private static void PopulateProperties(ISite site, SiteSettingsViewModel model) + if (!string.IsNullOrEmpty(site.BaseUrl) && !Uri.TryCreate(site.BaseUrl, UriKind.Absolute, out _)) { - model.SiteName = site.SiteName; - model.PageTitleFormat = site.PageTitleFormat; - model.BaseUrl = site.BaseUrl; - model.TimeZone = site.TimeZoneId; - model.PageSize = site.PageSize; - model.UseCdn = site.UseCdn; - model.CdnBaseUrl = site.CdnBaseUrl; - model.ResourceDebugMode = site.ResourceDebugMode; - model.AppendVersion = site.AppendVersion; - model.CacheMode = site.CacheMode; + context.Updater.ModelState.AddModelError(Prefix, nameof(model.BaseUrl), S["The Base url must be a fully qualified URL."]); } - private static bool IsGeneralGroup(BuildEditorContext context) - => context.GroupId.Equals(GroupId, StringComparison.OrdinalIgnoreCase); + _shellReleaseManager.RequestRelease(); + + return await EditAsync(site, context); } + + private static void PopulateProperties(ISite site, SiteSettingsViewModel model) + { + model.SiteName = site.SiteName; + model.PageTitleFormat = site.PageTitleFormat; + model.BaseUrl = site.BaseUrl; + model.TimeZone = site.TimeZoneId; + model.PageSize = site.PageSize; + model.UseCdn = site.UseCdn; + model.CdnBaseUrl = site.CdnBaseUrl; + model.ResourceDebugMode = site.ResourceDebugMode; + model.AppendVersion = site.AppendVersion; + model.CacheMode = site.CacheMode; + } + + private static bool IsGeneralGroup(BuildEditorContext context) + => context.GroupId.Equals(GroupId, StringComparison.OrdinalIgnoreCase); } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Liquid/LiquidSiteSettingsAccessor.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Liquid/LiquidSiteSettingsAccessor.cs index f6b16d41f36..7cb357c62fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Liquid/LiquidSiteSettingsAccessor.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Liquid/LiquidSiteSettingsAccessor.cs @@ -1,9 +1,8 @@ -namespace OrchardCore.Liquid +namespace OrchardCore.Liquid; + +/// +/// This is a placeholder class that allows modules to extend the `Site` property in the current Liquid scope. +/// +public class LiquidSiteSettingsAccessor { - /// - /// This is a placeholder class that allows modules to extend the `Site` property in the current Liquid scope. - /// - public class LiquidSiteSettingsAccessor - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Recipes/SettingsStep.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Recipes/SettingsStep.cs index 94c8240f571..3ce17d82af5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Recipes/SettingsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Recipes/SettingsStep.cs @@ -5,105 +5,104 @@ using OrchardCore.Recipes.Models; using OrchardCore.Recipes.Services; -namespace OrchardCore.Settings.Recipes +namespace OrchardCore.Settings.Recipes; + +/// +/// This recipe step updates the site settings. +/// +public sealed class SettingsStep : IRecipeStepHandler { - /// - /// This recipe step updates the site settings. - /// - public sealed class SettingsStep : IRecipeStepHandler + private readonly ISiteService _siteService; + + public SettingsStep(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public SettingsStep(ISiteService siteService) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Settings", StringComparison.OrdinalIgnoreCase)) { - _siteService = siteService; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + var model = context.Step; + var site = await _siteService.LoadSiteSettingsAsync(); + + foreach (var property in model) { - if (!string.Equals(context.Name, "Settings", StringComparison.OrdinalIgnoreCase)) + switch (property.Key) { - return; - } + case "BaseUrl": + site.BaseUrl = property.Value.ToString(); + break; - var model = context.Step; - var site = await _siteService.LoadSiteSettingsAsync(); + case "Calendar": + site.Calendar = property.Value.ToString(); + break; - foreach (var property in model) - { - switch (property.Key) - { - case "BaseUrl": - site.BaseUrl = property.Value.ToString(); - break; - - case "Calendar": - site.Calendar = property.Value.ToString(); - break; - - case "MaxPagedCount": - site.MaxPagedCount = property.Value.Value(); - break; - - case "MaxPageSize": - site.MaxPageSize = property.Value.Value(); - break; - - case "PageSize": - site.PageSize = property.Value.Value(); - break; - - case "ResourceDebugMode": - site.ResourceDebugMode = (ResourceDebugMode)property.Value.Value(); - break; - - case "SiteName": - site.SiteName = property.Value.ToString(); - break; - - case "PageTitleFormat": - site.PageTitleFormat = property.Value.ToString(); - break; - - case "SiteSalt": - site.SiteSalt = property.Value.ToString(); - break; - - case "SuperUser": - site.SuperUser = property.Value.ToString(); - break; - - case "TimeZoneId": - site.TimeZoneId = property.Value.ToString(); - break; - - case "UseCdn": - site.UseCdn = property.Value.Value(); - break; - - case "CdnBaseUrl": - site.CdnBaseUrl = property.Value.ToString(); - break; - - case "AppendVersion": - site.AppendVersion = property.Value.Value(); - break; - - case "HomeRoute": - site.HomeRoute = property.Value.ToObject(); - break; - - case "CacheMode": - site.CacheMode = (CacheMode)property.Value.Value(); - break; - - default: - site.Properties[property.Key] = property.Value.Clone(); - break; - } - } + case "MaxPagedCount": + site.MaxPagedCount = property.Value.Value(); + break; + + case "MaxPageSize": + site.MaxPageSize = property.Value.Value(); + break; + + case "PageSize": + site.PageSize = property.Value.Value(); + break; + + case "ResourceDebugMode": + site.ResourceDebugMode = (ResourceDebugMode)property.Value.Value(); + break; + + case "SiteName": + site.SiteName = property.Value.ToString(); + break; - await _siteService.UpdateSiteSettingsAsync(site); + case "PageTitleFormat": + site.PageTitleFormat = property.Value.ToString(); + break; + + case "SiteSalt": + site.SiteSalt = property.Value.ToString(); + break; + + case "SuperUser": + site.SuperUser = property.Value.ToString(); + break; + + case "TimeZoneId": + site.TimeZoneId = property.Value.ToString(); + break; + + case "UseCdn": + site.UseCdn = property.Value.Value(); + break; + + case "CdnBaseUrl": + site.CdnBaseUrl = property.Value.ToString(); + break; + + case "AppendVersion": + site.AppendVersion = property.Value.Value(); + break; + + case "HomeRoute": + site.HomeRoute = property.Value.ToObject(); + break; + + case "CacheMode": + site.CacheMode = (CacheMode)property.Value.Value(); + break; + + default: + site.Properties[property.Key] = property.Value.Clone(); + break; + } } + + await _siteService.UpdateSiteSettingsAsync(site); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Services/DefaultTimeZoneSelector.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Services/DefaultTimeZoneSelector.cs index ed6fb3ba700..8a064ff13c2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Services/DefaultTimeZoneSelector.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Services/DefaultTimeZoneSelector.cs @@ -1,29 +1,28 @@ using System.Threading.Tasks; using OrchardCore.Modules; -namespace OrchardCore.Settings.Services +namespace OrchardCore.Settings.Services; + +/// +/// Provides the timezone defined in the site configuration for the current scope (request). +/// The same is returned if called multiple times +/// during the same scope. +/// +public class DefaultTimeZoneSelector : ITimeZoneSelector { - /// - /// Provides the timezone defined in the site configuration for the current scope (request). - /// The same is returned if called multiple times - /// during the same scope. - /// - public class DefaultTimeZoneSelector : ITimeZoneSelector - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public DefaultTimeZoneSelector(ISiteService siteService) - { - _siteService = siteService; - } + public DefaultTimeZoneSelector(ISiteService siteService) + { + _siteService = siteService; + } - public Task GetTimeZoneAsync() + public Task GetTimeZoneAsync() + { + return Task.FromResult(new TimeZoneSelectorResult { - return Task.FromResult(new TimeZoneSelectorResult - { - Priority = 0, - TimeZoneId = async () => (await _siteService.GetSiteSettingsAsync())?.TimeZoneId - }); - } + Priority = 0, + TimeZoneId = async () => (await _siteService.GetSiteSettingsAsync())?.TimeZoneId + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Services/RecipeEnvironmentSiteNameProvider.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Services/RecipeEnvironmentSiteNameProvider.cs index 5d2b9243e29..1ab5f03d7c6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Services/RecipeEnvironmentSiteNameProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Services/RecipeEnvironmentSiteNameProvider.cs @@ -2,26 +2,25 @@ using System.Threading.Tasks; using OrchardCore.Recipes.Services; -namespace OrchardCore.Settings +namespace OrchardCore.Settings; + +public class RecipeEnvironmentSiteNameProvider : IRecipeEnvironmentProvider { - public class RecipeEnvironmentSiteNameProvider : IRecipeEnvironmentProvider - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public RecipeEnvironmentSiteNameProvider(ISiteService siteService) - { - _siteService = siteService; - } + public RecipeEnvironmentSiteNameProvider(ISiteService siteService) + { + _siteService = siteService; + } - public int Order => 0; + public int Order => 0; - public async Task PopulateEnvironmentAsync(IDictionary environment) + public async Task PopulateEnvironmentAsync(IDictionary environment) + { + var siteSettings = await _siteService.GetSiteSettingsAsync(); + if (!string.IsNullOrEmpty(siteSettings.SiteName)) { - var siteSettings = await _siteService.GetSiteSettingsAsync(); - if (!string.IsNullOrEmpty(siteSettings.SiteName)) - { - environment[nameof(SiteSettings.SiteName)] = siteSettings.SiteName; - } + environment[nameof(SiteSettings.SiteName)] = siteSettings.SiteName; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SetupEventHandler.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SetupEventHandler.cs index 111db9c0fba..56ea03cac9d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SetupEventHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SetupEventHandler.cs @@ -3,31 +3,30 @@ using OrchardCore.Setup.Events; using OrchardCore.Setup.Services; -namespace OrchardCore.Settings.Services +namespace OrchardCore.Settings.Services; + +/// +/// During setup, registers the Super User. +/// +public class SetupEventHandler : ISetupEventHandler { - /// - /// During setup, registers the Super User. - /// - public class SetupEventHandler : ISetupEventHandler - { - private readonly ISiteService _siteService; + private readonly ISiteService _siteService; - public SetupEventHandler(ISiteService siteService) - { - _siteService = siteService; - } + public SetupEventHandler(ISiteService siteService) + { + _siteService = siteService; + } - public async Task SetupAsync(SetupContext context) - { - // Updating site settings. - var siteSettings = await _siteService.LoadSiteSettingsAsync(); - siteSettings.SiteName = context.Properties.TryGetValue(SetupConstants.SiteName, out var siteName) ? siteName?.ToString() : string.Empty; - siteSettings.SuperUser = context.Properties.TryGetValue(SetupConstants.AdminUserId, out var adminUserId) ? adminUserId?.ToString() : string.Empty; - siteSettings.TimeZoneId = context.Properties.TryGetValue(SetupConstants.SiteTimeZone, out var siteTimeZone) ? siteTimeZone?.ToString() : string.Empty; + public async Task SetupAsync(SetupContext context) + { + // Updating site settings. + var siteSettings = await _siteService.LoadSiteSettingsAsync(); + siteSettings.SiteName = context.Properties.TryGetValue(SetupConstants.SiteName, out var siteName) ? siteName?.ToString() : string.Empty; + siteSettings.SuperUser = context.Properties.TryGetValue(SetupConstants.AdminUserId, out var adminUserId) ? adminUserId?.ToString() : string.Empty; + siteSettings.TimeZoneId = context.Properties.TryGetValue(SetupConstants.SiteTimeZone, out var siteTimeZone) ? siteTimeZone?.ToString() : string.Empty; - await _siteService.UpdateSiteSettingsAsync(siteSettings); + await _siteService.UpdateSiteSettingsAsync(siteSettings); - // TODO: Add Encryption Settings in. - } + // TODO: Add Encryption Settings in. } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SiteService.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SiteService.cs index afe59310951..984ec24d091 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SiteService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SiteService.cs @@ -3,65 +3,64 @@ using OrchardCore.Documents; using OrchardCore.Modules; -namespace OrchardCore.Settings.Services +namespace OrchardCore.Settings.Services; + +/// +/// Implements by storing the site settings as a document. +/// +public class SiteService : ISiteService { - /// - /// Implements by storing the site settings as a document. - /// - public class SiteService : ISiteService - { - private readonly IDocumentManager _documentManager; - private readonly IClock _clock; + private readonly IDocumentManager _documentManager; + private readonly IClock _clock; - public SiteService(IDocumentManager documentManager, IClock clock) - { - _documentManager = documentManager; - _clock = clock; - } + public SiteService(IDocumentManager documentManager, IClock clock) + { + _documentManager = documentManager; + _clock = clock; + } - /// - /// Loads the site settings from the store for updating and that should not be cached. - /// - // Await as we can't cast 'Task' to 'Task'. - public async Task LoadSiteSettingsAsync() - => await _documentManager.GetOrCreateMutableAsync(GetDefaultSettingsAsync); + /// + /// Loads the site settings from the store for updating and that should not be cached. + /// + // Await as we can't cast 'Task' to 'Task'. + public async Task LoadSiteSettingsAsync() + => await _documentManager.GetOrCreateMutableAsync(GetDefaultSettingsAsync); - /// - /// Gets the site settings from the cache for sharing and that should not be updated. - /// - // Await as we can't cast 'Task' to 'Task'. - public async Task GetSiteSettingsAsync() - => await _documentManager.GetOrCreateImmutableAsync(GetDefaultSettingsAsync); + /// + /// Gets the site settings from the cache for sharing and that should not be updated. + /// + // Await as we can't cast 'Task' to 'Task'. + public async Task GetSiteSettingsAsync() + => await _documentManager.GetOrCreateImmutableAsync(GetDefaultSettingsAsync); - /// - /// Updates the store with the provided site settings and then updates the cache. - /// - public async Task UpdateSiteSettingsAsync(ISite site) + /// + /// Updates the store with the provided site settings and then updates the cache. + /// + public async Task UpdateSiteSettingsAsync(ISite site) + { + if (site is not SiteSettings siteSettings) { - if (site is not SiteSettings siteSettings) - { - return; - } + return; + } - await _documentManager.UpdateAsync(siteSettings); + await _documentManager.UpdateAsync(siteSettings); - // Clear the internal cache to ensure that any other lookup against - // this document will load the new values until the site is reloaded. - siteSettings.ClearCache(); - } + // Clear the internal cache to ensure that any other lookup against + // this document will load the new values until the site is reloaded. + siteSettings.ClearCache(); + } - private Task GetDefaultSettingsAsync() + private Task GetDefaultSettingsAsync() + { + return Task.FromResult(new SiteSettings { - return Task.FromResult(new SiteSettings - { - SiteSalt = Guid.NewGuid().ToString("N"), - SiteName = "My Orchard Project Application", - PageTitleFormat = "{% page_title Site.SiteName, position: \"after\", separator: \" - \" %}", - TimeZoneId = _clock.GetSystemTimeZone().TimeZoneId, - PageSize = 10, - MaxPageSize = 100, - MaxPagedCount = 0 - }); - } + SiteSalt = Guid.NewGuid().ToString("N"), + SiteName = "My Orchard Project Application", + PageTitleFormat = "{% page_title Site.SiteName, position: \"after\", separator: \" - \" %}", + TimeZoneId = _clock.GetSystemTimeZone().TimeZoneId, + PageSize = 10, + MaxPageSize = 100, + MaxPagedCount = 0 + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SuperUserHandler.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SuperUserHandler.cs index ae37337a7a0..4567c0f38d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Services/SuperUserHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Services/SuperUserHandler.cs @@ -5,42 +5,41 @@ using Microsoft.AspNetCore.Authorization; using OrchardCore.Security; -namespace OrchardCore.Settings.Services +namespace OrchardCore.Settings.Services; + +/// +/// This authorization handler validates any permission when the user is the site owner. +/// +public class SuperUserHandler : IAuthorizationHandler { - /// - /// This authorization handler validates any permission when the user is the site owner. - /// - public class SuperUserHandler : IAuthorizationHandler + private readonly ISiteService _siteService; + + public SuperUserHandler(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public SuperUserHandler(ISiteService siteService) + public async Task HandleAsync(AuthorizationHandlerContext context) + { + var userId = context?.User?.FindFirstValue(ClaimTypes.NameIdentifier); + if (userId == null) { - _siteService = siteService; + return; } - public async Task HandleAsync(AuthorizationHandlerContext context) - { - var userId = context?.User?.FindFirstValue(ClaimTypes.NameIdentifier); - if (userId == null) - { - return; - } + var site = await _siteService.GetSiteSettingsAsync(); - var site = await _siteService.GetSiteSettingsAsync(); - - if (string.Equals(userId, site.SuperUser, StringComparison.OrdinalIgnoreCase)) - { - SucceedAllRequirements(context); - } + if (string.Equals(userId, site.SuperUser, StringComparison.OrdinalIgnoreCase)) + { + SucceedAllRequirements(context); } + } - private static void SucceedAllRequirements(AuthorizationHandlerContext context) + private static void SucceedAllRequirements(AuthorizationHandlerContext context) + { + foreach (var requirement in context.Requirements.OfType()) { - foreach (var requirement in context.Requirements.OfType()) - { - context.Succeed(requirement); - } + context.Succeed(requirement); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/SiteSettings.cs b/src/OrchardCore.Modules/OrchardCore.Settings/SiteSettings.cs index b47ca92b1e9..023c1dcfe96 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/SiteSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/SiteSettings.cs @@ -3,52 +3,51 @@ using OrchardCore.Documents; using OrchardCore.Entities; -namespace OrchardCore.Settings -{ - // When updating class also update SiteSettingsDeploymentSource and SettingsStep. - public class SiteSettings : DocumentEntity, ISite - { - private readonly ConcurrentDictionary _cache = new(); - - public string BaseUrl { get; set; } - public string Calendar { get; set; } - public int MaxPagedCount { get; set; } - public int MaxPageSize { get; set; } - public int PageSize { get; set; } - public string TimeZoneId { get; set; } - public ResourceDebugMode ResourceDebugMode { get; set; } - public string SiteName { get; set; } - public string SiteSalt { get; set; } - public string PageTitleFormat { get; set; } - public string SuperUser { get; set; } - public bool UseCdn { get; set; } - public string CdnBaseUrl { get; set; } - public RouteValueDictionary HomeRoute { get; set; } = []; - public bool AppendVersion { get; set; } = true; - public CacheMode CacheMode { get; set; } - - public T As() where T : new() - { - var name = typeof(T).Name; - if (!IsReadOnly) - { - return this.As(name); - } +namespace OrchardCore.Settings; - if (_cache.TryGetValue(name, out var obj) && obj is T value) - { - return value; - } +// When updating class also update SiteSettingsDeploymentSource and SettingsStep. +public class SiteSettings : DocumentEntity, ISite +{ + private readonly ConcurrentDictionary _cache = new(); - var settings = this.As(name); - _cache[name] = settings; + public string BaseUrl { get; set; } + public string Calendar { get; set; } + public int MaxPagedCount { get; set; } + public int MaxPageSize { get; set; } + public int PageSize { get; set; } + public string TimeZoneId { get; set; } + public ResourceDebugMode ResourceDebugMode { get; set; } + public string SiteName { get; set; } + public string SiteSalt { get; set; } + public string PageTitleFormat { get; set; } + public string SuperUser { get; set; } + public bool UseCdn { get; set; } + public string CdnBaseUrl { get; set; } + public RouteValueDictionary HomeRoute { get; set; } = []; + public bool AppendVersion { get; set; } = true; + public CacheMode CacheMode { get; set; } - return settings; + public T As() where T : new() + { + var name = typeof(T).Name; + if (!IsReadOnly) + { + return this.As(name); } - internal void ClearCache() + if (_cache.TryGetValue(name, out var obj) && obj is T value) { - _cache.Clear(); + return value; } + + var settings = this.As(name); + _cache[name] = settings; + + return settings; + } + + internal void ClearCache() + { + _cache.Clear(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Settings/Startup.cs index cee923e65b5..3ca02e824af 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/Startup.cs @@ -18,71 +18,70 @@ using OrchardCore.Settings.Services; using OrchardCore.Setup.Events; -namespace OrchardCore.Settings +namespace OrchardCore.Settings; + +/// +/// These services are registered on the tenant service collection. +/// +public sealed class Startup : StartupBase { - /// - /// These services are registered on the tenant service collection. - /// - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => + o.Scope.SetValue("Site", new ObjectValue(new LiquidSiteSettingsAccessor())); + o.MemberAccessStrategy.Register(async (obj, name, context) => { - o.Scope.SetValue("Site", new ObjectValue(new LiquidSiteSettingsAccessor())); - o.MemberAccessStrategy.Register(async (obj, name, context) => - { - var liquidTemplateContext = (LiquidTemplateContext)context; + var liquidTemplateContext = (LiquidTemplateContext)context; - var siteService = liquidTemplateContext.Services.GetRequiredService(); - var site = await siteService.GetSiteSettingsAsync(); + var siteService = liquidTemplateContext.Services.GetRequiredService(); + var site = await siteService.GetSiteSettingsAsync(); - FluidValue result = name switch - { - nameof(ISite.SiteName) => new StringValue(site.SiteName), - nameof(ISite.PageTitleFormat) => new StringValue(site.PageTitleFormat), - nameof(ISite.SiteSalt) => new StringValue(site.SiteSalt), - nameof(ISite.SuperUser) => new StringValue(site.SuperUser), - nameof(ISite.Calendar) => new StringValue(site.Calendar), - nameof(ISite.TimeZoneId) => new StringValue(site.TimeZoneId), - nameof(ISite.ResourceDebugMode) => new StringValue(site.ResourceDebugMode.ToString()), - nameof(ISite.UseCdn) => BooleanValue.Create(site.UseCdn), - nameof(ISite.CdnBaseUrl) => new StringValue(site.CdnBaseUrl), - nameof(ISite.PageSize) => NumberValue.Create(site.PageSize), - nameof(ISite.MaxPageSize) => NumberValue.Create(site.MaxPageSize), - nameof(ISite.MaxPagedCount) => NumberValue.Create(site.MaxPagedCount), - nameof(ISite.BaseUrl) => new StringValue(site.BaseUrl), - nameof(ISite.HomeRoute) => new ObjectValue(site.HomeRoute), - nameof(ISite.AppendVersion) => BooleanValue.Create(site.AppendVersion), - nameof(ISite.CacheMode) => new StringValue(site.CacheMode.ToString()), - nameof(ISite.Properties) => new ObjectValue(site.Properties), - _ => NilValue.Instance - }; + FluidValue result = name switch + { + nameof(ISite.SiteName) => new StringValue(site.SiteName), + nameof(ISite.PageTitleFormat) => new StringValue(site.PageTitleFormat), + nameof(ISite.SiteSalt) => new StringValue(site.SiteSalt), + nameof(ISite.SuperUser) => new StringValue(site.SuperUser), + nameof(ISite.Calendar) => new StringValue(site.Calendar), + nameof(ISite.TimeZoneId) => new StringValue(site.TimeZoneId), + nameof(ISite.ResourceDebugMode) => new StringValue(site.ResourceDebugMode.ToString()), + nameof(ISite.UseCdn) => BooleanValue.Create(site.UseCdn), + nameof(ISite.CdnBaseUrl) => new StringValue(site.CdnBaseUrl), + nameof(ISite.PageSize) => NumberValue.Create(site.PageSize), + nameof(ISite.MaxPageSize) => NumberValue.Create(site.MaxPageSize), + nameof(ISite.MaxPagedCount) => NumberValue.Create(site.MaxPagedCount), + nameof(ISite.BaseUrl) => new StringValue(site.BaseUrl), + nameof(ISite.HomeRoute) => new ObjectValue(site.HomeRoute), + nameof(ISite.AppendVersion) => BooleanValue.Create(site.AppendVersion), + nameof(ISite.CacheMode) => new StringValue(site.CacheMode.ToString()), + nameof(ISite.Properties) => new ObjectValue(site.Properties), + _ => NilValue.Instance + }; - return result; - }); + return result; }); + }); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); - services.AddSingleton(); + services.AddRecipeExecutionStep(); + services.AddSingleton(); - // Site Settings editor - services.AddScoped, DefaultSiteSettingsDisplayDriver>(); - services.AddScoped, ButtonsSettingsDisplayDriver>(); - services.AddScoped(); + // Site Settings editor + services.AddScoped, DefaultSiteSettingsDisplayDriver>(); + services.AddScoped, ButtonsSettingsDisplayDriver>(); + services.AddScoped(); - services.AddScoped(); + services.AddScoped(); - services.AddDeployment(); + services.AddDeployment(); - services.AddScoped(); + services.AddScoped(); - services.AddTransient, ResourceOptionsConfiguration>(); - services.AddTransient, PagerOptionsConfiguration>(); - } + services.AddTransient, ResourceOptionsConfiguration>(); + services.AddTransient, PagerOptionsConfiguration>(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/AdminIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/AdminIndexViewModel.cs index 7f98ae32787..6cb175bb21c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/AdminIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/AdminIndexViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Settings.ViewModels +namespace OrchardCore.Settings.ViewModels; + +public class AdminIndexViewModel { - public class AdminIndexViewModel - { - public dynamic Shape { get; set; } - public string GroupId { get; set; } - } + public dynamic Shape { get; set; } + public string GroupId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteCulturesViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteCulturesViewModel.cs index 4421fe1f726..5bdb12164e8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteCulturesViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteCulturesViewModel.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using System.Globalization; -namespace OrchardCore.Settings.ViewModels +namespace OrchardCore.Settings.ViewModels; + +public class SiteCulturesViewModel { - public class SiteCulturesViewModel - { - public string CurrentCulture { get; set; } - public IEnumerable SiteCultures { get; set; } - public IEnumerable AvailableSystemCultures { get; set; } - } + public string CurrentCulture { get; set; } + public IEnumerable SiteCultures { get; set; } + public IEnumerable AvailableSystemCultures { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsDeploymentStepViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsDeploymentStepViewModel.cs index 36039af06b6..edbba5ed8de 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsDeploymentStepViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsDeploymentStepViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Settings.ViewModels +namespace OrchardCore.Settings.ViewModels; + +public class SiteSettingsDeploymentStepViewModel { - public class SiteSettingsDeploymentStepViewModel - { - public string[] Settings { get; set; } - } + public string[] Settings { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsViewModel.cs index c4570fda419..d08a3c6679b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Settings/ViewModels/SiteSettingsViewModel.cs @@ -1,22 +1,21 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Settings.ViewModels +namespace OrchardCore.Settings.ViewModels; + +public class SiteSettingsViewModel { - public class SiteSettingsViewModel - { - public string SiteName { get; set; } - public string PageTitleFormat { get; set; } - public string BaseUrl { get; set; } - public string TimeZone { get; set; } + public string SiteName { get; set; } + public string PageTitleFormat { get; set; } + public string BaseUrl { get; set; } + public string TimeZone { get; set; } - [Required] - public int? PageSize { get; set; } + [Required] + public int? PageSize { get; set; } - public bool UseCdn { get; set; } - public string CdnBaseUrl { get; set; } - public ResourceDebugMode ResourceDebugMode { get; set; } - public bool AppendVersion { get; set; } - public string Meta { get; set; } - public CacheMode CacheMode { get; set; } - } + public bool UseCdn { get; set; } + public string CdnBaseUrl { get; set; } + public ResourceDebugMode ResourceDebugMode { get; set; } + public bool AppendVersion { get; set; } + public string Meta { get; set; } + public CacheMode CacheMode { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Setup/Controllers/SetupController.cs b/src/OrchardCore.Modules/OrchardCore.Setup/Controllers/SetupController.cs index d96ec02a46a..4d5ec7b0888 100644 --- a/src/OrchardCore.Modules/OrchardCore.Setup/Controllers/SetupController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Setup/Controllers/SetupController.cs @@ -18,248 +18,247 @@ using OrchardCore.Setup.Services; using OrchardCore.Setup.ViewModels; -namespace OrchardCore.Setup.Controllers +namespace OrchardCore.Setup.Controllers; + +public class SetupController : Controller { - public class SetupController : Controller + private readonly IClock _clock; + private readonly ISetupService _setupService; + private readonly ShellSettings _shellSettings; + private readonly IShellHost _shellHost; + private readonly IdentityOptions _identityOptions; + private readonly IEmailAddressValidator _emailAddressValidator; + private readonly IEnumerable _databaseProviders; + private readonly ILogger _logger; + protected readonly IStringLocalizer S; + + public SetupController( + IClock clock, + ISetupService setupService, + ShellSettings shellSettings, + IShellHost shellHost, + IOptions identityOptions, + IEmailAddressValidator emailAddressValidator, + IEnumerable databaseProviders, + IStringLocalizer localizer, + ILogger logger) + { + _clock = clock; + _setupService = setupService; + _shellSettings = shellSettings; + _shellHost = shellHost; + _identityOptions = identityOptions.Value; + _emailAddressValidator = emailAddressValidator; + _databaseProviders = databaseProviders; + _logger = logger; + S = localizer; + } + + public async Task Index(string token) { - private readonly IClock _clock; - private readonly ISetupService _setupService; - private readonly ShellSettings _shellSettings; - private readonly IShellHost _shellHost; - private readonly IdentityOptions _identityOptions; - private readonly IEmailAddressValidator _emailAddressValidator; - private readonly IEnumerable _databaseProviders; - private readonly ILogger _logger; - protected readonly IStringLocalizer S; - - public SetupController( - IClock clock, - ISetupService setupService, - ShellSettings shellSettings, - IShellHost shellHost, - IOptions identityOptions, - IEmailAddressValidator emailAddressValidator, - IEnumerable databaseProviders, - IStringLocalizer localizer, - ILogger logger) + var recipes = await _setupService.GetSetupRecipesAsync(); + var defaultRecipe = recipes.FirstOrDefault(x => x.Tags.Contains("default")) ?? recipes.FirstOrDefault(); + + if (!await ShouldProceedWithTokenAsync(token)) { - _clock = clock; - _setupService = setupService; - _shellSettings = shellSettings; - _shellHost = shellHost; - _identityOptions = identityOptions.Value; - _emailAddressValidator = emailAddressValidator; - _databaseProviders = databaseProviders; - _logger = logger; - S = localizer; + return NotFound(); } - public async Task Index(string token) + var model = new SetupViewModel { - var recipes = await _setupService.GetSetupRecipesAsync(); - var defaultRecipe = recipes.FirstOrDefault(x => x.Tags.Contains("default")) ?? recipes.FirstOrDefault(); - - if (!await ShouldProceedWithTokenAsync(token)) - { - return NotFound(); - } + DatabaseProviders = _databaseProviders, + Recipes = recipes, + RecipeName = defaultRecipe?.Name, + Secret = token, + }; - var model = new SetupViewModel - { - DatabaseProviders = _databaseProviders, - Recipes = recipes, - RecipeName = defaultRecipe?.Name, - Secret = token, - }; + CopyShellSettingsValues(model); - CopyShellSettingsValues(model); + if (!string.IsNullOrEmpty(_shellSettings["TablePrefix"])) + { + model.DatabaseConfigurationPreset = true; + model.TablePrefix = _shellSettings["TablePrefix"]; + } - if (!string.IsNullOrEmpty(_shellSettings["TablePrefix"])) - { - model.DatabaseConfigurationPreset = true; - model.TablePrefix = _shellSettings["TablePrefix"]; - } + if (!string.IsNullOrEmpty(_shellSettings["Schema"])) + { + model.DatabaseConfigurationPreset = true; + model.Schema = _shellSettings["Schema"]; + } - if (!string.IsNullOrEmpty(_shellSettings["Schema"])) - { - model.DatabaseConfigurationPreset = true; - model.Schema = _shellSettings["Schema"]; - } + return View(model); + } - return View(model); + [HttpPost, ActionName("Index")] + public async Task IndexPOST(SetupViewModel model) + { + if (!await ShouldProceedWithTokenAsync(model.Secret)) + { + return StatusCode(404); } - [HttpPost, ActionName("Index")] - public async Task IndexPOST(SetupViewModel model) + model.DatabaseProviders = _databaseProviders; + model.Recipes = await _setupService.GetSetupRecipesAsync(); + + if (string.IsNullOrEmpty(model.Password)) { - if (!await ShouldProceedWithTokenAsync(model.Secret)) - { - return StatusCode(404); - } + ModelState.AddModelError(nameof(model.Password), S["The password is required."]); + } - model.DatabaseProviders = _databaseProviders; - model.Recipes = await _setupService.GetSetupRecipesAsync(); + if (model.Password != model.PasswordConfirmation) + { + ModelState.AddModelError(nameof(model.PasswordConfirmation), S["The password confirmation doesn't match the password."]); + } - if (string.IsNullOrEmpty(model.Password)) + RecipeDescriptor selectedRecipe = null; + if (!string.IsNullOrEmpty(_shellSettings["RecipeName"])) + { + selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == _shellSettings["RecipeName"]); + if (selectedRecipe == null) { - ModelState.AddModelError(nameof(model.Password), S["The password is required."]); + ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]); } + } + else if (string.IsNullOrEmpty(model.RecipeName) || (selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == model.RecipeName)) == null) + { + ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]); + } - if (model.Password != model.PasswordConfirmation) - { - ModelState.AddModelError(nameof(model.PasswordConfirmation), S["The password confirmation doesn't match the password."]); - } + // Only add additional errors if attribute validation has passed. + if (!string.IsNullOrEmpty(model.Email) && !_emailAddressValidator.Validate(model.Email)) + { + ModelState.AddModelError(nameof(model.Email), S["The email is invalid."]); + } - RecipeDescriptor selectedRecipe = null; - if (!string.IsNullOrEmpty(_shellSettings["RecipeName"])) - { - selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == _shellSettings["RecipeName"]); - if (selectedRecipe == null) - { - ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]); - } - } - else if (string.IsNullOrEmpty(model.RecipeName) || (selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == model.RecipeName)) == null) - { - ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]); - } + if (!string.IsNullOrEmpty(model.UserName) && model.UserName.Any(c => !_identityOptions.User.AllowedUserNameCharacters.Contains(c))) + { + ModelState.AddModelError(nameof(model.UserName), S["User name '{0}' is invalid, can only contain letters or digits.", model.UserName]); + } - // Only add additional errors if attribute validation has passed. - if (!string.IsNullOrEmpty(model.Email) && !_emailAddressValidator.Validate(model.Email)) - { - ModelState.AddModelError(nameof(model.Email), S["The email is invalid."]); - } + if (!ModelState.IsValid) + { + CopyShellSettingsValues(model); + return View(model); + } - if (!string.IsNullOrEmpty(model.UserName) && model.UserName.Any(c => !_identityOptions.User.AllowedUserNameCharacters.Contains(c))) + var setupContext = new SetupContext + { + ShellSettings = _shellSettings, + EnabledFeatures = null, // default list, + Errors = new Dictionary(), + Recipe = selectedRecipe, + Properties = new Dictionary { - ModelState.AddModelError(nameof(model.UserName), S["User name '{0}' is invalid, can only contain letters or digits.", model.UserName]); + { SetupConstants.SiteName, model.SiteName }, + { SetupConstants.AdminUsername, model.UserName }, + { SetupConstants.AdminEmail, model.Email }, + { SetupConstants.AdminPassword, model.Password }, + { SetupConstants.SiteTimeZone, model.SiteTimeZone }, } + }; - if (!ModelState.IsValid) - { - CopyShellSettingsValues(model); - return View(model); - } + if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"])) + { + model.DatabaseConfigurationPreset = true; + setupContext.Properties[SetupConstants.DatabaseProvider] = _shellSettings["DatabaseProvider"]; + setupContext.Properties[SetupConstants.DatabaseConnectionString] = _shellSettings["ConnectionString"]; + setupContext.Properties[SetupConstants.DatabaseTablePrefix] = _shellSettings["TablePrefix"]; + setupContext.Properties[SetupConstants.DatabaseSchema] = _shellSettings["Schema"]; + } + else + { + setupContext.Properties[SetupConstants.DatabaseProvider] = model.DatabaseProvider; + setupContext.Properties[SetupConstants.DatabaseConnectionString] = model.ConnectionString; + setupContext.Properties[SetupConstants.DatabaseTablePrefix] = model.TablePrefix; + setupContext.Properties[SetupConstants.DatabaseSchema] = model.Schema; + } - var setupContext = new SetupContext - { - ShellSettings = _shellSettings, - EnabledFeatures = null, // default list, - Errors = new Dictionary(), - Recipe = selectedRecipe, - Properties = new Dictionary - { - { SetupConstants.SiteName, model.SiteName }, - { SetupConstants.AdminUsername, model.UserName }, - { SetupConstants.AdminEmail, model.Email }, - { SetupConstants.AdminPassword, model.Password }, - { SetupConstants.SiteTimeZone, model.SiteTimeZone }, - } - }; + var executionId = await _setupService.SetupAsync(setupContext); - if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"])) - { - model.DatabaseConfigurationPreset = true; - setupContext.Properties[SetupConstants.DatabaseProvider] = _shellSettings["DatabaseProvider"]; - setupContext.Properties[SetupConstants.DatabaseConnectionString] = _shellSettings["ConnectionString"]; - setupContext.Properties[SetupConstants.DatabaseTablePrefix] = _shellSettings["TablePrefix"]; - setupContext.Properties[SetupConstants.DatabaseSchema] = _shellSettings["Schema"]; - } - else + // Check if any Setup component failed (e.g., database connection validation) + if (setupContext.Errors.Count > 0) + { + foreach (var error in setupContext.Errors) { - setupContext.Properties[SetupConstants.DatabaseProvider] = model.DatabaseProvider; - setupContext.Properties[SetupConstants.DatabaseConnectionString] = model.ConnectionString; - setupContext.Properties[SetupConstants.DatabaseTablePrefix] = model.TablePrefix; - setupContext.Properties[SetupConstants.DatabaseSchema] = model.Schema; + ModelState.AddModelError(error.Key, error.Value); } - var executionId = await _setupService.SetupAsync(setupContext); - - // Check if any Setup component failed (e.g., database connection validation) - if (setupContext.Errors.Count > 0) - { - foreach (var error in setupContext.Errors) - { - ModelState.AddModelError(error.Key, error.Value); - } + return View(model); + } - return View(model); - } + return Redirect("~/"); + } - return Redirect("~/"); + private void CopyShellSettingsValues(SetupViewModel model) + { + if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"])) + { + model.DatabaseConfigurationPreset = true; + model.ConnectionString = _shellSettings["ConnectionString"]; } - private void CopyShellSettingsValues(SetupViewModel model) + if (!string.IsNullOrEmpty(_shellSettings["RecipeName"])) { - if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"])) - { - model.DatabaseConfigurationPreset = true; - model.ConnectionString = _shellSettings["ConnectionString"]; - } - - if (!string.IsNullOrEmpty(_shellSettings["RecipeName"])) - { - model.RecipeNamePreset = true; - model.RecipeName = _shellSettings["RecipeName"]; - } + model.RecipeNamePreset = true; + model.RecipeName = _shellSettings["RecipeName"]; + } - if (!string.IsNullOrEmpty(_shellSettings["DatabaseProvider"])) - { - model.DatabaseConfigurationPreset = true; - model.DatabaseProvider = _shellSettings["DatabaseProvider"]; - } - else - { - model.DatabaseProvider = model.DatabaseProviders.FirstOrDefault(p => p.IsDefault)?.Value; - } + if (!string.IsNullOrEmpty(_shellSettings["DatabaseProvider"])) + { + model.DatabaseConfigurationPreset = true; + model.DatabaseProvider = _shellSettings["DatabaseProvider"]; + } + else + { + model.DatabaseProvider = model.DatabaseProviders.FirstOrDefault(p => p.IsDefault)?.Value; } + } - private async Task ShouldProceedWithTokenAsync(string token) + private async Task ShouldProceedWithTokenAsync(string token) + { + if (!string.IsNullOrWhiteSpace(_shellSettings["Secret"])) { - if (!string.IsNullOrWhiteSpace(_shellSettings["Secret"])) + if (string.IsNullOrEmpty(token) || !await IsTokenValid(token)) { - if (string.IsNullOrEmpty(token) || !await IsTokenValid(token)) - { - _logger.LogWarning("An attempt to access '{TenantName}' without providing a secret was made", _shellSettings.Name); + _logger.LogWarning("An attempt to access '{TenantName}' without providing a secret was made", _shellSettings.Name); - return false; - } + return false; } - - return true; } - private async Task IsTokenValid(string token) + return true; + } + + private async Task IsTokenValid(string token) + { + var result = false; + try { - var result = false; - try - { - var shellScope = await _shellHost.GetScopeAsync(ShellSettings.DefaultShellName); + var shellScope = await _shellHost.GetScopeAsync(ShellSettings.DefaultShellName); - await shellScope.UsingAsync(scope => - { - var dataProtectionProvider = scope.ServiceProvider.GetRequiredService(); - var dataProtector = dataProtectionProvider.CreateProtector("Tokens").ToTimeLimitedDataProtector(); + await shellScope.UsingAsync(scope => + { + var dataProtectionProvider = scope.ServiceProvider.GetRequiredService(); + var dataProtector = dataProtectionProvider.CreateProtector("Tokens").ToTimeLimitedDataProtector(); - var tokenValue = dataProtector.Unprotect(token, out var expiration); + var tokenValue = dataProtector.Unprotect(token, out var expiration); - if (_clock.UtcNow < expiration.ToUniversalTime()) + if (_clock.UtcNow < expiration.ToUniversalTime()) + { + if (_shellSettings["Secret"] == tokenValue) { - if (_shellSettings["Secret"] == tokenValue) - { - result = true; - } + result = true; } + } - return Task.CompletedTask; - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in decrypting the token"); - } - - return result; + return Task.CompletedTask; + }); } + catch (Exception ex) + { + _logger.LogError(ex, "Error in decrypting the token"); + } + + return result; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Setup/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Setup/Startup.cs index a0dcdb7b6ca..0c6c8171791 100644 --- a/src/OrchardCore.Modules/OrchardCore.Setup/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Setup/Startup.cs @@ -13,87 +13,86 @@ using OrchardCore.Localization; using OrchardCore.Modules; -namespace OrchardCore.Setup +namespace OrchardCore.Setup; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + private readonly string _defaultCulture = CultureInfo.InstalledUICulture.Name; + + private string[] _supportedCultures = + [ + "ar", + "cs", + "de", + "el", + "en", + "es", + "fa", + "fr", + "it", + "ja", + "pl", + "pt-BR", + "ru", + "sv", + "tr", + "vi", + "zh-CN", + "zh-TW", + "zh-Hans-CN", + "zh-Hant-TW" + ]; + + public Startup(IShellConfiguration shellConfiguration) { - private readonly string _defaultCulture = CultureInfo.InstalledUICulture.Name; + var configurationSection = shellConfiguration.GetSection("OrchardCore_Setup"); - private string[] _supportedCultures = - [ - "ar", - "cs", - "de", - "el", - "en", - "es", - "fa", - "fr", - "it", - "ja", - "pl", - "pt-BR", - "ru", - "sv", - "tr", - "vi", - "zh-CN", - "zh-TW", - "zh-Hans-CN", - "zh-Hant-TW" - ]; + _defaultCulture = configurationSection["DefaultCulture"] ?? _defaultCulture; + _supportedCultures = configurationSection.GetSection("SupportedCultures").Get>()?.ToArray() ?? _supportedCultures; + } - public Startup(IShellConfiguration shellConfiguration) - { - var configurationSection = shellConfiguration.GetSection("OrchardCore_Setup"); + public override void ConfigureServices(IServiceCollection services) + { + services.AddPortableObjectLocalization(options => options.ResourcesPath = "Localization"); + services.Replace(ServiceDescriptor.Singleton()); - _defaultCulture = configurationSection["DefaultCulture"] ?? _defaultCulture; - _supportedCultures = configurationSection.GetSection("SupportedCultures").Get>()?.ToArray() ?? _supportedCultures; - } + services.AddSetup(); - public override void ConfigureServices(IServiceCollection services) + services.Configure(options => { - services.AddPortableObjectLocalization(options => options.ResourcesPath = "Localization"); - services.Replace(ServiceDescriptor.Singleton()); + options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+"; + options.User.RequireUniqueEmail = true; + }); + } - services.AddSetup(); + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + { + var localizationOptions = serviceProvider.GetService>().Value; + var cultureOptions = serviceProvider.GetService>().Value; - services.Configure(options => - { - options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+"; - options.User.RequireUniqueEmail = true; - }); - } + localizationOptions.CultureInfoUseUserOverride = !cultureOptions.IgnoreSystemSettings; - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + if (!string.IsNullOrEmpty(_defaultCulture)) { - var localizationOptions = serviceProvider.GetService>().Value; - var cultureOptions = serviceProvider.GetService>().Value; - - localizationOptions.CultureInfoUseUserOverride = !cultureOptions.IgnoreSystemSettings; - - if (!string.IsNullOrEmpty(_defaultCulture)) - { - localizationOptions.SetDefaultCulture(_defaultCulture); + localizationOptions.SetDefaultCulture(_defaultCulture); - _supportedCultures = _supportedCultures.Union(new[] { _defaultCulture }).ToArray(); - } + _supportedCultures = _supportedCultures.Union(new[] { _defaultCulture }).ToArray(); + } - if (_supportedCultures?.Length > 0) - { - localizationOptions - .AddSupportedCultures(_supportedCultures) - .AddSupportedUICultures(_supportedCultures); - } + if (_supportedCultures?.Length > 0) + { + localizationOptions + .AddSupportedCultures(_supportedCultures) + .AddSupportedUICultures(_supportedCultures); + } - app.UseRequestLocalization(localizationOptions); + app.UseRequestLocalization(localizationOptions); - routes.MapAreaControllerRoute( - name: "Setup", - areaName: "OrchardCore.Setup", - pattern: "", - defaults: new { controller = "Setup", action = "Index" } - ); - } + routes.MapAreaControllerRoute( + name: "Setup", + areaName: "OrchardCore.Setup", + pattern: "", + defaults: new { controller = "Setup", action = "Index" } + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Setup/ViewModels/SetupViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Setup/ViewModels/SetupViewModel.cs index d872a88bcf4..94e26b2b46c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Setup/ViewModels/SetupViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Setup/ViewModels/SetupViewModel.cs @@ -4,54 +4,53 @@ using OrchardCore.Data; using OrchardCore.Recipes.Models; -namespace OrchardCore.Setup.ViewModels +namespace OrchardCore.Setup.ViewModels; + +public class SetupViewModel { - public class SetupViewModel - { - [Required] - [StringLength(70)] - public string SiteName { get; set; } + [Required] + [StringLength(70)] + public string SiteName { get; set; } - public string Description { get; set; } + public string Description { get; set; } - public string DatabaseProvider { get; set; } + public string DatabaseProvider { get; set; } - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } - public string TablePrefix { get; set; } + public string TablePrefix { get; set; } - public string Schema { get; set; } + public string Schema { get; set; } - /// - /// True if the database configuration is preset and can't be changed or displayed on the Setup screen. - /// - [BindNever] - public bool DatabaseConfigurationPreset { get; set; } + /// + /// True if the database configuration is preset and can't be changed or displayed on the Setup screen. + /// + [BindNever] + public bool DatabaseConfigurationPreset { get; set; } - [Required] - public string UserName { get; set; } + [Required] + public string UserName { get; set; } - [Required] - public string Email { get; set; } + [Required] + public string Email { get; set; } - [DataType(DataType.Password)] - public string Password { get; set; } + [DataType(DataType.Password)] + public string Password { get; set; } - [DataType(DataType.Password)] - public string PasswordConfirmation { get; set; } + [DataType(DataType.Password)] + public string PasswordConfirmation { get; set; } - [BindNever] - public IEnumerable DatabaseProviders { get; set; } = []; + [BindNever] + public IEnumerable DatabaseProviders { get; set; } = []; - [BindNever] - public IEnumerable Recipes { get; set; } + [BindNever] + public IEnumerable Recipes { get; set; } - public bool RecipeNamePreset { get; set; } + public bool RecipeNamePreset { get; set; } - public string RecipeName { get; set; } + public string RecipeName { get; set; } - public string SiteTimeZone { get; set; } + public string SiteTimeZone { get; set; } - public string Secret { get; set; } - } + public string Secret { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/AdminMenu.cs index 4258ca7c78c..0c1a5c6dc04 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/AdminMenu.cs @@ -2,34 +2,33 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Shortcodes +namespace OrchardCore.Shortcodes; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Design"], design => design - .Add(S["Shortcodes"], S["Shortcodes"].PrefixPosition(), import => import - .Action("Index", "Admin", "OrchardCore.Shortcodes") - .Permission(Permissions.ManageShortcodeTemplates) - .LocalNav() - ) - ); + builder + .Add(S["Design"], design => design + .Add(S["Shortcodes"], S["Shortcodes"].PrefixPosition(), import => import + .Action("Index", "Admin", "OrchardCore.Shortcodes") + .Permission(Permissions.ManageShortcodeTemplates) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs index 4024dc02aa9..c8be761bebe 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Controllers/AdminController.cs @@ -23,336 +23,335 @@ using OrchardCore.Shortcodes.ViewModels; using Parlot; -namespace OrchardCore.Shortcodes.Controllers +namespace OrchardCore.Shortcodes.Controllers; + +[Feature("OrchardCore.Shortcodes.Templates")] +public class AdminController : Controller { - [Feature("OrchardCore.Shortcodes.Templates")] - public class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + private readonly IHtmlSanitizerService _htmlSanitizerService; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + IAuthorizationService authorizationService, + ShortcodeTemplatesManager shortcodeTemplatesManager, + ILiquidTemplateManager liquidTemplateManager, + IOptions pagerOptions, + INotifier notifier, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + IHtmlSanitizerService htmlSanitizerService + ) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - private readonly IHtmlSanitizerService _htmlSanitizerService; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - IAuthorizationService authorizationService, - ShortcodeTemplatesManager shortcodeTemplatesManager, - ILiquidTemplateManager liquidTemplateManager, - IOptions pagerOptions, - INotifier notifier, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - IHtmlSanitizerService htmlSanitizerService - ) + _authorizationService = authorizationService; + _shortcodeTemplatesManager = shortcodeTemplatesManager; + _liquidTemplateManager = liquidTemplateManager; + _pagerOptions = pagerOptions.Value; + _notifier = notifier; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + _htmlSanitizerService = htmlSanitizerService; + } + + [Admin("Shortcodes", "Shortcodes.Index")] + public async Task Index(ContentOptions options, PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) { - _authorizationService = authorizationService; - _shortcodeTemplatesManager = shortcodeTemplatesManager; - _liquidTemplateManager = liquidTemplateManager; - _pagerOptions = pagerOptions.Value; - _notifier = notifier; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - _htmlSanitizerService = htmlSanitizerService; + return Forbid(); } - [Admin("Shortcodes", "Shortcodes.Index")] - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); + var shortcodeTemplates = shortcodeTemplatesDocument.ShortcodeTemplates.ToList(); - var shortcodeTemplates = shortcodeTemplatesDocument.ShortcodeTemplates.ToList(); + if (!string.IsNullOrWhiteSpace(options.Search)) + { + shortcodeTemplates = shortcodeTemplates.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - if (!string.IsNullOrWhiteSpace(options.Search)) - { - shortcodeTemplates = shortcodeTemplates.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + var count = shortcodeTemplates.Count; - var count = shortcodeTemplates.Count; + shortcodeTemplates = shortcodeTemplates.OrderBy(x => x.Key) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize).ToList(); - shortcodeTemplates = shortcodeTemplates.OrderBy(x => x.Key) - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize).ToList(); + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var model = new ShortcodeTemplateIndexViewModel + { + ShortcodeTemplates = shortcodeTemplates.Select(x => new ShortcodeTemplateEntry { Name = x.Key, ShortcodeTemplate = x.Value }).ToList(), + Options = options, + Pager = pagerShape + }; - var model = new ShortcodeTemplateIndexViewModel - { - ShortcodeTemplates = shortcodeTemplates.Select(x => new ShortcodeTemplateEntry { Name = x.Key, ShortcodeTemplate = x.Value }).ToList(), - Options = options, - Pager = pagerShape - }; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + return View(model); + } + + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(ShortcodeTemplateIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - return View(model); + [Admin("Shortcodes/Create", "Shortcodes.Create")] + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(ShortcodeTemplateIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + return View(new ShortcodeTemplateViewModel()); + } - [Admin("Shortcodes/Create", "Shortcodes.Create")] - public async Task Create() + [HttpPost, ActionName(nameof(Create))] + public async Task CreatePost(ShortcodeTemplateViewModel model, string submit) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) - { - return Forbid(); - } - - return View(new ShortcodeTemplateViewModel()); + return Forbid(); } - [HttpPost, ActionName(nameof(Create))] - public async Task CreatePost(ShortcodeTemplateViewModel model, string submit) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name is mandatory."]); } - - if (ModelState.IsValid) + else if (!IsValidShortcodeName(model.Name)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name is mandatory."]); - } - else if (!IsValidShortcodeName(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name contains invalid characters."]); - } - else - { - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - - if (shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["A template with the same name already exists."]); - } - } + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name contains invalid characters."]); + } + else + { + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - if (string.IsNullOrEmpty(model.Content)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template content is mandatory."]); - } - else if (!_liquidTemplateManager.Validate(model.Content, out var errors)) + if (shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(model.Name)) { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["A template with the same name already exists."]); } } - if (ModelState.IsValid) + if (string.IsNullOrEmpty(model.Content)) { - var template = new ShortcodeTemplate - { - Content = model.Content, - Hint = model.Hint, - Usage = _htmlSanitizerService.Sanitize(model.Usage), - DefaultValue = model.DefaultValue, - Categories = JConvert.DeserializeObject(model.SelectedCategories) - }; + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template content is mandatory."]); + } + else if (!_liquidTemplateManager.Validate(model.Content, out var errors)) + { + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + } - await _shortcodeTemplatesManager.UpdateShortcodeTemplateAsync(model.Name, template); + if (ModelState.IsValid) + { + var template = new ShortcodeTemplate + { + Content = model.Content, + Hint = model.Hint, + Usage = _htmlSanitizerService.Sanitize(model.Usage), + DefaultValue = model.DefaultValue, + Categories = JConvert.DeserializeObject(model.SelectedCategories) + }; - if (submit == "SaveAndContinue") - { - return RedirectToAction(nameof(Edit), new { name = model.Name }); - } + await _shortcodeTemplatesManager.UpdateShortcodeTemplateAsync(model.Name, template); - return RedirectToAction(nameof(Index)); + if (submit == "SaveAndContinue") + { + return RedirectToAction(nameof(Edit), new { name = model.Name }); } - // If we got this far, something failed, redisplay form - return View(model); + return RedirectToAction(nameof(Index)); } - [Admin("Shortcodes/Edit/{name}", "Shortcodes.Edit")] - public async Task Edit(string name) + // If we got this far, something failed, redisplay form + return View(model); + } + + [Admin("Shortcodes/Edit/{name}", "Shortcodes.Edit")] + public async Task Edit(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) - { - return Forbid(); - } + return Forbid(); + } - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - if (!shortcodeTemplatesDocument.ShortcodeTemplates.TryGetValue(name, out var template)) - { - return RedirectToAction(nameof(Create), new { name }); - } + if (!shortcodeTemplatesDocument.ShortcodeTemplates.TryGetValue(name, out var template)) + { + return RedirectToAction(nameof(Create), new { name }); + } - var model = new ShortcodeTemplateViewModel - { - Name = name, - Content = template.Content, - Hint = template.Hint, - Usage = template.Usage, - DefaultValue = template.DefaultValue, - Categories = template.Categories - }; + var model = new ShortcodeTemplateViewModel + { + Name = name, + Content = template.Content, + Hint = template.Hint, + Usage = template.Usage, + DefaultValue = template.DefaultValue, + Categories = template.Categories + }; + + return View(model); + } - return View(model); + [HttpPost] + public async Task Edit(string sourceName, ShortcodeTemplateViewModel model, string submit) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) + { + return Forbid(); } - [HttpPost] - public async Task Edit(string sourceName, ShortcodeTemplateViewModel model, string submit) + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); + + if (!shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(sourceName)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) + return NotFound(); + } + + if (ModelState.IsValid) + { + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name is mandatory."]); } - - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); - - if (!shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(sourceName)) + else if (!IsValidShortcodeName(model.Name)) { - return NotFound(); + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name contains invalid characters."]); } - - if (ModelState.IsValid) + else if (!string.Equals(model.Name, sourceName, StringComparison.OrdinalIgnoreCase) + && shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(model.Name)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name is mandatory."]); - } - else if (!IsValidShortcodeName(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["The name contains invalid characters."]); - } - else if (!string.Equals(model.Name, sourceName, StringComparison.OrdinalIgnoreCase) - && shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(model.Name)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["A template with the same name already exists."]); - } - - if (string.IsNullOrEmpty(model.Content)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template content is mandatory."]); - } - else if (!_liquidTemplateManager.Validate(model.Content, out var errors)) - { - ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); - } + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Name), S["A template with the same name already exists."]); } - if (ModelState.IsValid) + if (string.IsNullOrEmpty(model.Content)) { - var template = new ShortcodeTemplate - { - Content = model.Content, - Hint = model.Hint, - Usage = _htmlSanitizerService.Sanitize(model.Usage), - DefaultValue = model.DefaultValue, - Categories = JConvert.DeserializeObject(model.SelectedCategories) - }; + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template content is mandatory."]); + } + else if (!_liquidTemplateManager.Validate(model.Content, out var errors)) + { + ModelState.AddModelError(nameof(ShortcodeTemplateViewModel.Content), S["The template doesn't contain a valid Liquid expression. Details: {0}", string.Join(" ", errors)]); + } + } - await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(sourceName); + if (ModelState.IsValid) + { + var template = new ShortcodeTemplate + { + Content = model.Content, + Hint = model.Hint, + Usage = _htmlSanitizerService.Sanitize(model.Usage), + DefaultValue = model.DefaultValue, + Categories = JConvert.DeserializeObject(model.SelectedCategories) + }; - await _shortcodeTemplatesManager.UpdateShortcodeTemplateAsync(model.Name, template); + await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(sourceName); - if (submit == "SaveAndContinue") - { - return RedirectToAction(nameof(Edit), new { name = model.Name }); - } + await _shortcodeTemplatesManager.UpdateShortcodeTemplateAsync(model.Name, template); - return RedirectToAction(nameof(Index)); + if (submit == "SaveAndContinue") + { + return RedirectToAction(nameof(Edit), new { name = model.Name }); } - // If we got this far, something failed, redisplay form + return RedirectToAction(nameof(Index)); + } - // If the name was changed or removed, prevent a 404 or a failure on the next post. - model.Name = sourceName; + // If we got this far, something failed, redisplay form - return View(model); - } + // If the name was changed or removed, prevent a 404 or a failure on the next post. + model.Name = sourceName; + + return View(model); + } - [HttpPost] - public async Task Delete(string name) + [HttpPost] + public async Task Delete(string name) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) - { - return Forbid(); - } + return Forbid(); + } - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); - if (!shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(name)) - { - return NotFound(); - } + if (!shortcodeTemplatesDocument.ShortcodeTemplates.ContainsKey(name)) + { + return NotFound(); + } - await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(name); + await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(name); - await _notifier.SuccessAsync(H["Shortcode template deleted successfully."]); + await _notifier.SuccessAsync(H["Shortcode template deleted successfully."]); - return RedirectToAction(nameof(Index)); - } + return RedirectToAction(nameof(Index)); + } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.BulkAction")] - public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.BulkAction")] + public async Task IndexPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageShortcodeTemplates)) - { - return Forbid(); - } + return Forbid(); + } - if (itemIds?.Count() > 0) + if (itemIds?.Count() > 0) + { + var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); + var checkedContentItems = shortcodeTemplatesDocument.ShortcodeTemplates.Where(x => itemIds.Contains(x.Key)); + switch (options.BulkAction) { - var shortcodeTemplatesDocument = await _shortcodeTemplatesManager.LoadShortcodeTemplatesDocumentAsync(); - var checkedContentItems = shortcodeTemplatesDocument.ShortcodeTemplates.Where(x => itemIds.Contains(x.Key)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(item.Key); - } - await _notifier.SuccessAsync(H["Shortcode templates successfully removed."]); - break; - default: - throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _shortcodeTemplatesManager.RemoveShortcodeTemplateAsync(item.Key); + } + await _notifier.SuccessAsync(H["Shortcode templates successfully removed."]); + break; + default: + throw new ArgumentOutOfRangeException(options.BulkAction.ToString(), "Invalid bulk action."); } - - return RedirectToAction(nameof(Index)); } - private static bool IsValidShortcodeName(string name) - { - var scanner = new Scanner(name); - return scanner.ReadIdentifier(out var result) && name.Length == result.Length; - } + return RedirectToAction(nameof(Index)); + } + + private static bool IsValidShortcodeName(string name) + { + var scanner = new Scanner(name); + return scanner.ReadIdentifier(out var result) && name.Length == result.Length; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentSource.cs index 277093f5b0d..f79c8af83c9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentSource.cs @@ -3,37 +3,36 @@ using OrchardCore.Deployment; using OrchardCore.Shortcodes.Services; -namespace OrchardCore.Shortcodes.Deployment +namespace OrchardCore.Shortcodes.Deployment; + +public class AllShortcodeTemplatesDeploymentSource : IDeploymentSource { - public class AllShortcodeTemplatesDeploymentSource : IDeploymentSource + private readonly ShortcodeTemplatesManager _templatesManager; + + public AllShortcodeTemplatesDeploymentSource(ShortcodeTemplatesManager templatesManager) { - private readonly ShortcodeTemplatesManager _templatesManager; + _templatesManager = templatesManager; + } - public AllShortcodeTemplatesDeploymentSource(ShortcodeTemplatesManager templatesManager) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AllShortcodeTemplatesDeploymentStep) { - _templatesManager = templatesManager; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not AllShortcodeTemplatesDeploymentStep) - { - return; - } - - var templateObjects = new JsonObject(); - var templates = await _templatesManager.GetShortcodeTemplatesDocumentAsync(); - - foreach (var template in templates.ShortcodeTemplates) - { - templateObjects[template.Key] = JObject.FromObject(template.Value); - } + var templateObjects = new JsonObject(); + var templates = await _templatesManager.GetShortcodeTemplatesDocumentAsync(); - result.Steps.Add(new JsonObject - { - ["name"] = "ShortcodeTemplates", - ["ShortcodeTemplates"] = templateObjects, - }); + foreach (var template in templates.ShortcodeTemplates) + { + templateObjects[template.Key] = JObject.FromObject(template.Value); } + + result.Steps.Add(new JsonObject + { + ["name"] = "ShortcodeTemplates", + ["ShortcodeTemplates"] = templateObjects, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStep.cs index e9683eec35b..ef3461ed2ce 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStep.cs @@ -1,12 +1,11 @@ using OrchardCore.Deployment; -namespace OrchardCore.Shortcodes.Deployment +namespace OrchardCore.Shortcodes.Deployment; + +public class AllShortcodeTemplatesDeploymentStep : DeploymentStep { - public class AllShortcodeTemplatesDeploymentStep : DeploymentStep + public AllShortcodeTemplatesDeploymentStep() { - public AllShortcodeTemplatesDeploymentStep() - { - Name = "AllShortcodeTemplates"; - } + Name = "AllShortcodeTemplates"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStepDriver.cs index 6f5315bd3d5..dc718f6a3b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Deployment/AllShortcodeTemplatesDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Shortcodes.Deployment +namespace OrchardCore.Shortcodes.Deployment; + +public sealed class AllShortcodeTemplatesDeploymentStepDriver : DisplayDriver { - public sealed class AllShortcodeTemplatesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllShortcodeTemplatesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllShortcodeTemplatesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllShortcodeTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllShortcodeTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllShortcodeTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllShortcodeTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllShortcodeTemplatesDeploymentStep step, BuildEditorContext context) - { - return View("AllShortcodeTemplatesDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllShortcodeTemplatesDeploymentStep step, BuildEditorContext context) + { + return View("AllShortcodeTemplatesDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Drivers/ShortcodeDescriptorDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Drivers/ShortcodeDescriptorDisplayDriver.cs index 5c7e45b364f..36db09e49fd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Drivers/ShortcodeDescriptorDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Drivers/ShortcodeDescriptorDisplayDriver.cs @@ -2,16 +2,15 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Shortcodes.Drivers +namespace OrchardCore.Shortcodes.Drivers; + +public sealed class ShortcodeDescriptorDisplayDriver : DisplayDriver { - public sealed class ShortcodeDescriptorDisplayDriver : DisplayDriver + public override Task DisplayAsync(ShortcodeDescriptor descriptor, BuildDisplayContext context) { - public override Task DisplayAsync(ShortcodeDescriptor descriptor, BuildDisplayContext context) - { - return CombineAsync( - View("ShortcodeDescriptor_Fields_SummaryAdmin", descriptor).Location("SummaryAdmin", "Content"), - View("ShortcodeDescriptor_SummaryAdmin__Button__Actions", descriptor).Location("SummaryAdmin", "Actions") - ); - } + return CombineAsync( + View("ShortcodeDescriptor_Fields_SummaryAdmin", descriptor).Location("SummaryAdmin", "Content"), + View("ShortcodeDescriptor_SummaryAdmin__Button__Actions", descriptor).Location("SummaryAdmin", "Actions") + ); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Models/ShortcodeTemplatesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Models/ShortcodeTemplatesDocument.cs index 4265670120f..c03b55acecc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Models/ShortcodeTemplatesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Models/ShortcodeTemplatesDocument.cs @@ -2,19 +2,18 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Shortcodes.Models +namespace OrchardCore.Shortcodes.Models; + +public class ShortcodeTemplatesDocument : Document { - public class ShortcodeTemplatesDocument : Document - { - public Dictionary ShortcodeTemplates { get; init; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + public Dictionary ShortcodeTemplates { get; init; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +} - public class ShortcodeTemplate - { - public string Content { get; set; } - public string Hint { get; set; } - public string Usage { get; set; } - public string DefaultValue { get; set; } - public string[] Categories { get; set; } = []; - } +public class ShortcodeTemplate +{ + public string Content { get; set; } + public string Hint { get; set; } + public string Usage { get; set; } + public string DefaultValue { get; set; } + public string[] Categories { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Providers/LocaleShortcodeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Providers/LocaleShortcodeProvider.cs index bd3f3af1f4f..0613edbe4d4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Providers/LocaleShortcodeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Providers/LocaleShortcodeProvider.cs @@ -3,52 +3,51 @@ using System.Threading.Tasks; using Shortcodes; -namespace OrchardCore.Shortcodes.Providers +namespace OrchardCore.Shortcodes.Providers; + +public class LocaleShortcodeProvider : IShortcodeProvider { - public class LocaleShortcodeProvider : IShortcodeProvider - { - public const string ShortCodeIdentifier = "locale"; + public const string ShortCodeIdentifier = "locale"; - private static ValueTask Empty => new(string.Empty); - private static ValueTask Null => new((string)null); + private static ValueTask Empty => new(string.Empty); + private static ValueTask Null => new((string)null); - public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + { + if (identifier != ShortCodeIdentifier) { - if (identifier != ShortCodeIdentifier) - { - return Null; - } + return Null; + } - var language = arguments.NamedOrDefault("lang")?.ToLower(); - var argFallback = arguments.NamedOrAt("fallback", 1); + var language = arguments.NamedOrDefault("lang")?.ToLower(); + var argFallback = arguments.NamedOrAt("fallback", 1); - // Default value of true for the fallback argument. - var fallback = argFallback == null || Convert.ToBoolean(argFallback); - var currentCulture = CultureInfo.CurrentUICulture; + // Default value of true for the fallback argument. + var fallback = argFallback == null || Convert.ToBoolean(argFallback); + var currentCulture = CultureInfo.CurrentUICulture; - if (fallback) - { - // Fallback to parent culture, if the current culture is en-CA and the shortcode targets en, the html will be output. - do - { - if (currentCulture.Name.Equals(language, StringComparison.OrdinalIgnoreCase)) - { - return new ValueTask(content); - } - - currentCulture = currentCulture.Parent; - } - while (currentCulture != CultureInfo.InvariantCulture); - } - else + if (fallback) + { + // Fallback to parent culture, if the current culture is en-CA and the shortcode targets en, the html will be output. + do { if (currentCulture.Name.Equals(language, StringComparison.OrdinalIgnoreCase)) { return new ValueTask(content); } - } - return Empty; + currentCulture = currentCulture.Parent; + } + while (currentCulture != CultureInfo.InvariantCulture); } + else + { + if (currentCulture.Name.Equals(language, StringComparison.OrdinalIgnoreCase)) + { + return new ValueTask(content); + } + } + + return Empty; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Recipes/ShortcodeTemplateStep.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Recipes/ShortcodeTemplateStep.cs index e622011d772..0090fc955fa 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Recipes/ShortcodeTemplateStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Recipes/ShortcodeTemplateStep.cs @@ -6,36 +6,35 @@ using OrchardCore.Shortcodes.Models; using OrchardCore.Shortcodes.Services; -namespace OrchardCore.Shortcodes.Recipes +namespace OrchardCore.Shortcodes.Recipes; + +/// +/// This recipe step creates a set of shortcodes. +/// +public sealed class ShortcodeTemplateStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of shortcodes. - /// - public sealed class ShortcodeTemplateStep : IRecipeStepHandler + private readonly ShortcodeTemplatesManager _templatesManager; + + public ShortcodeTemplateStep(ShortcodeTemplatesManager templatesManager) { - private readonly ShortcodeTemplatesManager _templatesManager; + _templatesManager = templatesManager; + } - public ShortcodeTemplateStep(ShortcodeTemplatesManager templatesManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "ShortcodeTemplates", StringComparison.OrdinalIgnoreCase)) { - _templatesManager = templatesManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + if (context.Step.TryGetPropertyValue("ShortcodeTemplates", out var jsonNode) && jsonNode is JsonObject templates) { - if (!string.Equals(context.Name, "ShortcodeTemplates", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (context.Step.TryGetPropertyValue("ShortcodeTemplates", out var jsonNode) && jsonNode is JsonObject templates) + foreach (var property in templates) { - foreach (var property in templates) - { - var name = property.Key; - var value = property.Value.ToObject(); + var name = property.Key; + var value = property.Value.ToObject(); - await _templatesManager.UpdateShortcodeTemplateAsync(name, value); - } + await _templatesManager.UpdateShortcodeTemplateAsync(name, value); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/DefaultShortcodeContextProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/DefaultShortcodeContextProvider.cs index 390c84c9d77..96ff5a92cd7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/DefaultShortcodeContextProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/DefaultShortcodeContextProvider.cs @@ -1,23 +1,22 @@ using System; using Shortcodes; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +/// +/// Provides a default context to the shortcode processor. +/// +public class DefaultShortcodeContextProvider : IShortcodeContextProvider { - /// - /// Provides a default context to the shortcode processor. - /// - public class DefaultShortcodeContextProvider : IShortcodeContextProvider - { - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _serviceProvider; - public DefaultShortcodeContextProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public DefaultShortcodeContextProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } - public void Contextualize(Context context) - { - context["ServiceProvider"] = _serviceProvider; - } + public void Contextualize(Context context) + { + context["ServiceProvider"] = _serviceProvider; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/OptionsShortcodeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/OptionsShortcodeProvider.cs index 473681d6c2b..d518a1e9310 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/OptionsShortcodeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/OptionsShortcodeProvider.cs @@ -3,32 +3,31 @@ using Microsoft.Extensions.Options; using Shortcodes; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class OptionsShortcodeProvider : IShortcodeProvider { - public class OptionsShortcodeProvider : IShortcodeProvider - { - private static ValueTask Null => new((string)null); + private static ValueTask Null => new((string)null); - private readonly ShortcodeOptions _options; + private readonly ShortcodeOptions _options; - public OptionsShortcodeProvider(IOptions options) - { - _options = options.Value; - } + public OptionsShortcodeProvider(IOptions options) + { + _options = options.Value; + } - public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + public ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + { + if (_options.ShortcodeDelegates.TryGetValue(identifier, out var shortcode)) { - if (_options.ShortcodeDelegates.TryGetValue(identifier, out var shortcode)) + if (shortcode == null) { - if (shortcode == null) - { - return Null; - } - - return shortcode.Invoke(arguments, content, context); + return Null; } - return Null; + return shortcode.Invoke(arguments, content, context); } + + return Null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeDescriptorManager.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeDescriptorManager.cs index af46b281729..1364cb285f2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeDescriptorManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeDescriptorManager.cs @@ -3,36 +3,35 @@ using System.Linq; using System.Threading.Tasks; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class ShortcodeDescriptorManager : IShortcodeDescriptorManager { - public class ShortcodeDescriptorManager : IShortcodeDescriptorManager - { - private readonly IEnumerable _shortcodeDescriptorProviders; + private readonly IEnumerable _shortcodeDescriptorProviders; - public ShortcodeDescriptorManager(IEnumerable shortcodeDescriptorProviders) - { - _shortcodeDescriptorProviders = shortcodeDescriptorProviders; - } + public ShortcodeDescriptorManager(IEnumerable shortcodeDescriptorProviders) + { + _shortcodeDescriptorProviders = shortcodeDescriptorProviders; + } - public async Task> GetShortcodeDescriptors() - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + public async Task> GetShortcodeDescriptors() + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - // During discover providers are reversed so that first registered wins. - // This allows the templates feature to override code based shortcodes. - var reversedShortcodeDescriptorProviders = _shortcodeDescriptorProviders.Reverse(); + // During discover providers are reversed so that first registered wins. + // This allows the templates feature to override code based shortcodes. + var reversedShortcodeDescriptorProviders = _shortcodeDescriptorProviders.Reverse(); - foreach (var provider in reversedShortcodeDescriptorProviders) + foreach (var provider in reversedShortcodeDescriptorProviders) + { + var descriptors = await provider.DiscoverAsync(); + foreach (var descriptor in descriptors) { - var descriptors = await provider.DiscoverAsync(); - foreach (var descriptor in descriptors) - { - // Overwrite existing descriptors if they have been replaced. - result[descriptor.Name] = descriptor; - } + // Overwrite existing descriptors if they have been replaced. + result[descriptor.Name] = descriptor; } - - return result.Values; } + + return result.Values; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeOptionsDescriptorProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeOptionsDescriptorProvider.cs index 33e99b44826..a71db07c424 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeOptionsDescriptorProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeOptionsDescriptorProvider.cs @@ -3,26 +3,25 @@ using System.Threading.Tasks; using Microsoft.Extensions.Options; -namespace OrchardCore.Shortcodes.Services -{ - public class ShortcodeOptionsDescriptorProvider : IShortcodeDescriptorProvider - { - private readonly ShortcodeOptions _options; +namespace OrchardCore.Shortcodes.Services; - public ShortcodeOptionsDescriptorProvider(IOptions options) - { - _options = options.Value; - } +public class ShortcodeOptionsDescriptorProvider : IShortcodeDescriptorProvider +{ + private readonly ShortcodeOptions _options; - public Task> DiscoverAsync() => - Task.FromResult(_options.Shortcodes.Values.Select(option => - new ShortcodeDescriptor - { - Name = option.Name, - DefaultValue = option.DefaultValue, - Usage = option.Usage, - Hint = option.Hint, - Categories = option.Categories - })); + public ShortcodeOptionsDescriptorProvider(IOptions options) + { + _options = options.Value; } + + public Task> DiscoverAsync() => + Task.FromResult(_options.Shortcodes.Values.Select(option => + new ShortcodeDescriptor + { + Name = option.Name, + DefaultValue = option.DefaultValue, + Usage = option.Usage, + Hint = option.Hint, + Categories = option.Categories + })); } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeService.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeService.cs index d7e021ea3bd..2ede20777b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeService.cs @@ -2,31 +2,30 @@ using System.Threading.Tasks; using Shortcodes; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class ShortcodeService : IShortcodeService { - public class ShortcodeService : IShortcodeService + private readonly IEnumerable _shortcodeContextProviders; + private readonly ShortcodesProcessor _shortcodesProcessor; + + public ShortcodeService( + IEnumerable shortcodeProviders, + IEnumerable shortcodeContextProviders) { - private readonly IEnumerable _shortcodeContextProviders; - private readonly ShortcodesProcessor _shortcodesProcessor; + _shortcodesProcessor = new ShortcodesProcessor(shortcodeProviders); + _shortcodeContextProviders = shortcodeContextProviders; + } - public ShortcodeService( - IEnumerable shortcodeProviders, - IEnumerable shortcodeContextProviders) - { - _shortcodesProcessor = new ShortcodesProcessor(shortcodeProviders); - _shortcodeContextProviders = shortcodeContextProviders; - } + public ValueTask ProcessAsync(string input, Context context = null) + { + context ??= new Context(); - public ValueTask ProcessAsync(string input, Context context = null) + foreach (var contextProvider in _shortcodeContextProviders) { - context ??= new Context(); - - foreach (var contextProvider in _shortcodeContextProviders) - { - contextProvider.Contextualize(context); - } - - return _shortcodesProcessor.EvaluateAsync(input, context); + contextProvider.Contextualize(context); } + + return _shortcodesProcessor.EvaluateAsync(input, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesDescriptorProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesDescriptorProvider.cs index 782edc62a19..d1e71f470b1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesDescriptorProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesDescriptorProvider.cs @@ -2,30 +2,29 @@ using System.Linq; using System.Threading.Tasks; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class ShortcodeTemplatesDescriptorProvider : IShortcodeDescriptorProvider { - public class ShortcodeTemplatesDescriptorProvider : IShortcodeDescriptorProvider - { - private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; + private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; - public ShortcodeTemplatesDescriptorProvider(ShortcodeTemplatesManager shortcodeTemplatesManager) - { - _shortcodeTemplatesManager = shortcodeTemplatesManager; - } + public ShortcodeTemplatesDescriptorProvider(ShortcodeTemplatesManager shortcodeTemplatesManager) + { + _shortcodeTemplatesManager = shortcodeTemplatesManager; + } - public async Task> DiscoverAsync() - { - var document = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); + public async Task> DiscoverAsync() + { + var document = await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - return document.ShortcodeTemplates.Select(kvp => - new ShortcodeDescriptor - { - Name = kvp.Key, - Hint = kvp.Value.Hint, - DefaultValue = kvp.Value.DefaultValue, - Usage = kvp.Value.Usage, - Categories = kvp.Value.Categories - }); - } + return document.ShortcodeTemplates.Select(kvp => + new ShortcodeDescriptor + { + Name = kvp.Key, + Hint = kvp.Value.Hint, + DefaultValue = kvp.Value.DefaultValue, + Usage = kvp.Value.Usage, + Categories = kvp.Value.Categories + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesManager.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesManager.cs index d95d6d459e9..75fc30ad6af 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/ShortcodeTemplatesManager.cs @@ -2,36 +2,35 @@ using OrchardCore.Documents; using OrchardCore.Shortcodes.Models; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class ShortcodeTemplatesManager { - public class ShortcodeTemplatesManager - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public ShortcodeTemplatesManager(IDocumentManager documentManager) => _documentManager = documentManager; + public ShortcodeTemplatesManager(IDocumentManager documentManager) => _documentManager = documentManager; - /// - /// Loads the shortcode templates document from the store for updating and that should not be cached. - /// - public Task LoadShortcodeTemplatesDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); + /// + /// Loads the shortcode templates document from the store for updating and that should not be cached. + /// + public Task LoadShortcodeTemplatesDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); - /// - /// Gets the shortcode templates document from the cache for sharing and that should not be updated. - /// - public Task GetShortcodeTemplatesDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); + /// + /// Gets the shortcode templates document from the cache for sharing and that should not be updated. + /// + public Task GetShortcodeTemplatesDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); - public async Task RemoveShortcodeTemplateAsync(string name) - { - var document = await LoadShortcodeTemplatesDocumentAsync(); - document.ShortcodeTemplates.Remove(name); - await _documentManager.UpdateAsync(document); - } + public async Task RemoveShortcodeTemplateAsync(string name) + { + var document = await LoadShortcodeTemplatesDocumentAsync(); + document.ShortcodeTemplates.Remove(name); + await _documentManager.UpdateAsync(document); + } - public async Task UpdateShortcodeTemplateAsync(string name, ShortcodeTemplate template) - { - var document = await LoadShortcodeTemplatesDocumentAsync(); - document.ShortcodeTemplates[name.ToLower()] = template; - await _documentManager.UpdateAsync(document); - } + public async Task UpdateShortcodeTemplateAsync(string name, ShortcodeTemplate template) + { + var document = await LoadShortcodeTemplatesDocumentAsync(); + document.ShortcodeTemplates[name.ToLower()] = template; + await _documentManager.UpdateAsync(document); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/TemplateShortcodeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/TemplateShortcodeProvider.cs index 61462f10414..38683dee25b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/TemplateShortcodeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Services/TemplateShortcodeProvider.cs @@ -7,69 +7,68 @@ using OrchardCore.Shortcodes.ViewModels; using Shortcodes; -namespace OrchardCore.Shortcodes.Services +namespace OrchardCore.Shortcodes.Services; + +public class TemplateShortcodeProvider : IShortcodeProvider { - public class TemplateShortcodeProvider : IShortcodeProvider - { - private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; - private readonly ILiquidTemplateManager _liquidTemplateManager; - private readonly HtmlEncoder _htmlEncoder; + private readonly ShortcodeTemplatesManager _shortcodeTemplatesManager; + private readonly ILiquidTemplateManager _liquidTemplateManager; + private readonly HtmlEncoder _htmlEncoder; - private ShortcodeTemplatesDocument _shortcodeTemplatesDocument; - private readonly HashSet _identifiers = []; + private ShortcodeTemplatesDocument _shortcodeTemplatesDocument; + private readonly HashSet _identifiers = []; + + public TemplateShortcodeProvider( + ShortcodeTemplatesManager shortcodeTemplatesManager, + ILiquidTemplateManager liquidTemplateManager, + HtmlEncoder htmlEncoder) + { + _shortcodeTemplatesManager = shortcodeTemplatesManager; + _liquidTemplateManager = liquidTemplateManager; + _htmlEncoder = htmlEncoder; + } - public TemplateShortcodeProvider( - ShortcodeTemplatesManager shortcodeTemplatesManager, - ILiquidTemplateManager liquidTemplateManager, - HtmlEncoder htmlEncoder) + public async ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + { + _shortcodeTemplatesDocument ??= await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); + if (!_shortcodeTemplatesDocument.ShortcodeTemplates.TryGetValue(identifier, out var template)) { - _shortcodeTemplatesManager = shortcodeTemplatesManager; - _liquidTemplateManager = liquidTemplateManager; - _htmlEncoder = htmlEncoder; + return null; } - public async ValueTask EvaluateAsync(string identifier, Arguments arguments, string content, Context context) + // Check if a shortcode template is recursively called. + if (!_identifiers.Add(identifier)) { - _shortcodeTemplatesDocument ??= await _shortcodeTemplatesManager.GetShortcodeTemplatesDocumentAsync(); - if (!_shortcodeTemplatesDocument.ShortcodeTemplates.TryGetValue(identifier, out var template)) - { - return null; - } - - // Check if a shortcode template is recursively called. - if (!_identifiers.Add(identifier)) - { - return null; - } + return null; + } - var model = new ShortcodeViewModel - { - Args = arguments, - Content = content, - Context = context, - }; + var model = new ShortcodeViewModel + { + Args = arguments, + Content = content, + Context = context, + }; - var parameters = new Dictionary - { - [identifier] = new StringValue(""), - ["Args"] = new ObjectValue(model.Args), - ["Content"] = new ObjectValue(new Content(model.Content)), - ["Context"] = new ObjectValue(model.Context) - }; + var parameters = new Dictionary + { + [identifier] = new StringValue(""), + ["Args"] = new ObjectValue(model.Args), + ["Content"] = new ObjectValue(new Content(model.Content)), + ["Context"] = new ObjectValue(model.Context) + }; - var result = await _liquidTemplateManager.RenderStringAsync(template.Content, _htmlEncoder, model, parameters); + var result = await _liquidTemplateManager.RenderStringAsync(template.Content, _htmlEncoder, model, parameters); - // Allow multiple serial calls of this shortcode template. - _identifiers.Remove(identifier); + // Allow multiple serial calls of this shortcode template. + _identifiers.Remove(identifier); - return result; - } + return result; + } - internal sealed class Content : LiquidContentAccessor - { - public readonly string _content; - public Content(string content) => _content = content; - public override string ToString() => _content; - } + internal sealed class Content : LiquidContentAccessor + { + public readonly string _content; + public Content(string content) => _content = content; + public override string ToString() => _content; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Startup.cs index 24cbedf8f08..8c9b473fd0b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/Startup.cs @@ -15,73 +15,73 @@ using OrchardCore.Shortcodes.ViewModels; using Sc = Shortcodes; -namespace OrchardCore.Shortcodes +namespace OrchardCore.Shortcodes; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register((obj, name) => obj[name]); + o.MemberAccessStrategy.Register((obj, name) => obj[name]); - o.ValueConverters.Add(x => + o.ValueConverters.Add(x => + { + return x switch { - return x switch - { - // Prevent Context from being converted to an ArrayValue as it implements IEnumerable - Sc.Context c => new ObjectValue(c), - // Prevent Arguments from being converted to an ArrayValue as it implements IEnumerable - Sc.Arguments a => new ObjectValue(a), - _ => null - }; - }); - - o.MemberAccessStrategy.Register((obj, name) => obj.Named(name)); + // Prevent Context from being converted to an ArrayValue as it implements IEnumerable + Sc.Context c => new ObjectValue(c), + // Prevent Arguments from being converted to an ArrayValue as it implements IEnumerable + Sc.Arguments a => new ObjectValue(a), + _ => null + }; }); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + o.MemberAccessStrategy.Register((obj, name) => obj.Named(name)); + }); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddOptions(); - services.AddScoped(); - services.AddScoped, ShortcodeDescriptorDisplayDriver>(); - } + services.AddOptions(); + services.AddScoped(); + services.AddScoped, ShortcodeDescriptorDisplayDriver>(); } +} - [Feature("OrchardCore.Shortcodes.Templates")] - public sealed class ShortcodeTemplatesStartup : StartupBase - { - // Register this first so the templates provide overrides for any code driven shortcodes. - public override int Order => -10; +[Feature("OrchardCore.Shortcodes.Templates")] +public sealed class ShortcodeTemplatesStartup : StartupBase +{ + // Register this first so the templates provide overrides for any code driven shortcodes. + public override int Order => -10; - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - services.AddRecipeExecutionStep(); + services.AddRecipeExecutionStep(); - services.AddScoped(); - services.AddScoped(); - } + services.AddScoped(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Localization")] - public sealed class LocaleShortcodeProviderStartup : StartupBase +[RequireFeatures("OrchardCore.Localization")] +public sealed class LocaleShortcodeProviderStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.AddShortcode("locale", d => { - services.AddShortcode("locale", d => - { - d.DefaultValue = "[locale {language_code}] [/locale]"; - d.Hint = "Conditionally render content in the specified language"; - d.Usage = + d.DefaultValue = "[locale {language_code}] [/locale]"; + d.Hint = "Conditionally render content in the specified language"; + d.Usage = @"[locale en]English Text[/locale][locale fr false]French Text[/locale]
@@ -89,17 +89,16 @@ public override void ConfigureServices(IServiceCollection services)
lang, fallback
"; - d.Categories = ["Localization"]; - }); - } + d.Categories = ["Localization"]; + }); } +} - [RequireFeatures("OrchardCore.Deployment", "OrchardCore.Shortcodes.Templates")] - public sealed class ShortcodeTemplatesDeploymentStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment", "OrchardCore.Shortcodes.Templates")] +public sealed class ShortcodeTemplatesDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddDeployment(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateIndexViewModel.cs index aef1f518de6..fcbfe39f2ec 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateIndexViewModel.cs @@ -3,38 +3,37 @@ using Microsoft.AspNetCore.Mvc.Rendering; using OrchardCore.Shortcodes.Models; -namespace OrchardCore.Shortcodes.ViewModels +namespace OrchardCore.Shortcodes.ViewModels; + +public class ShortcodeTemplateIndexViewModel +{ + public IList ShortcodeTemplates { get; set; } + public dynamic Pager { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); +} + +public class ShortcodeTemplateEntry +{ + public string Name { get; set; } + public ShortcodeTemplate ShortcodeTemplate { get; set; } + public bool IsChecked { get; set; } +} + +public class ContentOptions +{ + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + + #endregion Lists to populate +} + +public enum ContentsBulkAction { - public class ShortcodeTemplateIndexViewModel - { - public IList ShortcodeTemplates { get; set; } - public dynamic Pager { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - } - - public class ShortcodeTemplateEntry - { - public string Name { get; set; } - public ShortcodeTemplate ShortcodeTemplate { get; set; } - public bool IsChecked { get; set; } - } - - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } - - #region Lists to populate - - [BindNever] - public List ContentsBulkAction { get; set; } - - #endregion Lists to populate - } - - public enum ContentsBulkAction - { - None, - Remove - } + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateViewModel.cs index 3e44e892dd8..0cd5a5d1933 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeTemplateViewModel.cs @@ -1,13 +1,12 @@ -namespace OrchardCore.Shortcodes.ViewModels +namespace OrchardCore.Shortcodes.ViewModels; + +public class ShortcodeTemplateViewModel { - public class ShortcodeTemplateViewModel - { - public string Name { get; set; } - public string Content { get; set; } - public string Hint { get; set; } - public string Usage { get; set; } - public string DefaultValue { get; set; } - public string[] Categories { get; set; } = []; - public string SelectedCategories { get; set; } - } + public string Name { get; set; } + public string Content { get; set; } + public string Hint { get; set; } + public string Usage { get; set; } + public string DefaultValue { get; set; } + public string[] Categories { get; set; } = []; + public string SelectedCategories { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeViewModel.cs index fe5f72cb389..4a74e5b28d9 100644 --- a/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Shortcodes/ViewModels/ShortcodeViewModel.cs @@ -1,11 +1,10 @@ using Shortcodes; -namespace OrchardCore.Shortcodes.ViewModels +namespace OrchardCore.Shortcodes.ViewModels; + +public class ShortcodeViewModel { - public class ShortcodeViewModel - { - public Arguments Args { get; set; } - public string Content { get; set; } - public Context Context { get; set; } - } + public Arguments Args { get; set; } + public string Content { get; set; } + public Context Context { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/AdminMenu.cs index a6fe6eb2cdb..462e8d2c843 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/AdminMenu.cs @@ -2,44 +2,43 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Sitemaps +namespace OrchardCore.Sitemaps; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["SEO"], S["SEO"].PrefixPosition(), seo => seo - .Permission(Permissions.ManageSitemaps) - .Add(S["Sitemaps"], S["Sitemaps"].PrefixPosition("1"), sitemaps => sitemaps - .Action("List", "Admin", "OrchardCore.Sitemaps") - .LocalNav() - ) - .Add(S["Sitemap Indexes"], S["Sitemap Indexes"].PrefixPosition("2"), indexes => indexes - .Action("List", "SitemapIndex", "OrchardCore.Sitemaps") - .LocalNav() - ) - .Add(S["Sitemaps Cache"], S["Sitemaps Cache"].PrefixPosition("3"), cache => cache - .Action("List", "SitemapCache", "OrchardCore.Sitemaps") - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["SEO"], S["SEO"].PrefixPosition(), seo => seo + .Permission(Permissions.ManageSitemaps) + .Add(S["Sitemaps"], S["Sitemaps"].PrefixPosition("1"), sitemaps => sitemaps + .Action("List", "Admin", "OrchardCore.Sitemaps") + .LocalNav() + ) + .Add(S["Sitemap Indexes"], S["Sitemap Indexes"].PrefixPosition("2"), indexes => indexes + .Action("List", "SitemapIndex", "OrchardCore.Sitemaps") + .LocalNav() + ) + .Add(S["Sitemaps Cache"], S["Sitemaps Cache"].PrefixPosition("3"), cache => cache + .Action("List", "SitemapCache", "OrchardCore.Sitemaps") + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceBuilder.cs index db63b1e2105..f2c1468ab4b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceBuilder.cs @@ -3,74 +3,73 @@ using System.Xml.Linq; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class CustomPathSitemapSourceBuilder : SitemapSourceBuilderBase { - public class CustomPathSitemapSourceBuilder : SitemapSourceBuilderBase + private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; + + public override async Task BuildSourceAsync(CustomPathSitemapSource source, SitemapBuilderContext context) { - private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; + var url = new XElement(_namespace + "url"); - public override async Task BuildSourceAsync(CustomPathSitemapSource source, SitemapBuilderContext context) + if (await BuildUrlsetMetadataAsync(source, context, url)) { - var url = new XElement(_namespace + "url"); - - if (await BuildUrlsetMetadataAsync(source, context, url)) - { - context.Response.ResponseElement.Add(url); - } + context.Response.ResponseElement.Add(url); } + } - private static Task BuildUrlsetMetadataAsync(CustomPathSitemapSource source, SitemapBuilderContext context, XElement url) + private static Task BuildUrlsetMetadataAsync(CustomPathSitemapSource source, SitemapBuilderContext context, XElement url) + { + if (BuildUrl(context, source, url)) { - if (BuildUrl(context, source, url)) - { - PopulateLastMod(source, url); - PopulateChangeFrequencyPriority(source, url); + PopulateLastMod(source, url); + PopulateChangeFrequencyPriority(source, url); - return Task.FromResult(true); - } - - return Task.FromResult(false); + return Task.FromResult(true); } - private static bool BuildUrl(SitemapBuilderContext context, CustomPathSitemapSource source, XElement url) - { - if (string.IsNullOrEmpty(source.Path)) - return false; + return Task.FromResult(false); + } - // Add ~/ to the path, because the it is inserted without leading /. - var path = "~/" + source.Path; + private static bool BuildUrl(SitemapBuilderContext context, CustomPathSitemapSource source, XElement url) + { + if (string.IsNullOrEmpty(source.Path)) + return false; - var loc = new XElement(_namespace + "loc"); - loc.Add(context.HostPrefix + context.UrlHelper.Content(path)); - url.Add(loc); - return true; - } + // Add ~/ to the path, because the it is inserted without leading /. + var path = "~/" + source.Path; - private static void PopulateChangeFrequencyPriority(CustomPathSitemapSource source, XElement url) - { - var changeFrequencyValue = source.ChangeFrequency.ToString(); - var priorityIntValue = source.Priority; + var loc = new XElement(_namespace + "loc"); + loc.Add(context.HostPrefix + context.UrlHelper.Content(path)); + url.Add(loc); + return true; + } + + private static void PopulateChangeFrequencyPriority(CustomPathSitemapSource source, XElement url) + { + var changeFrequencyValue = source.ChangeFrequency.ToString(); + var priorityIntValue = source.Priority; - var priorityValue = (priorityIntValue * 0.1f).ToString(CultureInfo.InvariantCulture); + var priorityValue = (priorityIntValue * 0.1f).ToString(CultureInfo.InvariantCulture); - var changeFreq = new XElement(_namespace + "changefreq"); - changeFreq.Add(changeFrequencyValue.ToLower()); - url.Add(changeFreq); + var changeFreq = new XElement(_namespace + "changefreq"); + changeFreq.Add(changeFrequencyValue.ToLower()); + url.Add(changeFreq); - var priority = new XElement(_namespace + "priority"); - priority.Add(priorityValue); - url.Add(priority); - } + var priority = new XElement(_namespace + "priority"); + priority.Add(priorityValue); + url.Add(priority); + } - private static void PopulateLastMod(CustomPathSitemapSource source, XElement url) + private static void PopulateLastMod(CustomPathSitemapSource source, XElement url) + { + // Last modified is not required. Do not include if the path has no modified date. + if (source.LastUpdate.HasValue) { - // Last modified is not required. Do not include if the path has no modified date. - if (source.LastUpdate.HasValue) - { - var lastMod = new XElement(_namespace + "lastmod"); - lastMod.Add(source.LastUpdate.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); - url.Add(lastMod); - } + var lastMod = new XElement(_namespace + "lastmod"); + lastMod.Add(source.LastUpdate.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); + url.Add(lastMod); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceModifiedDateProvider.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceModifiedDateProvider.cs index e8bedf44f18..1d1a0e32466 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceModifiedDateProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/CustomPathSitemapSourceModifiedDateProvider.cs @@ -2,13 +2,12 @@ using System.Threading.Tasks; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class CustomPathSitemapSourceModifiedDateProvider : SitemapSourceModifiedDateProviderBase { - public class CustomPathSitemapSourceModifiedDateProvider : SitemapSourceModifiedDateProviderBase + public override Task GetLastModifiedDateAsync(CustomPathSitemapSource source) { - public override Task GetLastModifiedDateAsync(CustomPathSitemapSource source) - { - return Task.FromResult(source.LastUpdate); - } + return Task.FromResult(source.LastUpdate); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapBuilder.cs index a32b8350e8f..24511668d8f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapBuilder.cs @@ -3,36 +3,35 @@ using System.Xml.Linq; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class DefaultSitemapBuilder : ISitemapBuilder { - public class DefaultSitemapBuilder : ISitemapBuilder + private readonly IEnumerable _sitemapTypeBuilders; + + public DefaultSitemapBuilder(IEnumerable sitemapTypeBuilders) { - private readonly IEnumerable _sitemapTypeBuilders; + _sitemapTypeBuilders = sitemapTypeBuilders; + } - public DefaultSitemapBuilder(IEnumerable sitemapTypeBuilders) + public async Task BuildAsync(SitemapType sitemap, SitemapBuilderContext context) + { + if (!sitemap.Enabled) { - _sitemapTypeBuilders = sitemapTypeBuilders; + return null; } - public async Task BuildAsync(SitemapType sitemap, SitemapBuilderContext context) + foreach (var sitemapTypeBuilder in _sitemapTypeBuilders) { - if (!sitemap.Enabled) + await sitemapTypeBuilder.BuildAsync(sitemap, context); + if (context.Response != null) { - return null; - } + var document = new XDocument(context.Response.ResponseElement); - foreach (var sitemapTypeBuilder in _sitemapTypeBuilders) - { - await sitemapTypeBuilder.BuildAsync(sitemap, context); - if (context.Response != null) - { - var document = new XDocument(context.Response.ResponseElement); - - return new XDocument(document); - } + return new XDocument(document); } - - return null; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapModifiedDateProvider.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapModifiedDateProvider.cs index e65e74a240b..c56e46bc27b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapModifiedDateProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/DefaultSitemapModifiedDateProvider.cs @@ -3,33 +3,32 @@ using System.Threading.Tasks; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class DefaultSitemapModifiedDateProvider : ISitemapModifiedDateProvider { - public class DefaultSitemapModifiedDateProvider : ISitemapModifiedDateProvider - { - private readonly IEnumerable _sitemapSourceModifiedDateProviders; + private readonly IEnumerable _sitemapSourceModifiedDateProviders; - public DefaultSitemapModifiedDateProvider(IEnumerable sitemapSourceModifiedDateProviders) - { - _sitemapSourceModifiedDateProviders = sitemapSourceModifiedDateProviders; - } + public DefaultSitemapModifiedDateProvider(IEnumerable sitemapSourceModifiedDateProviders) + { + _sitemapSourceModifiedDateProviders = sitemapSourceModifiedDateProviders; + } - public async Task GetLastModifiedDateAsync(SitemapType sitemap) + public async Task GetLastModifiedDateAsync(SitemapType sitemap) + { + DateTime? lastModifiedDate = null; + foreach (var source in sitemap.SitemapSources) { - DateTime? lastModifiedDate = null; - foreach (var source in sitemap.SitemapSources) + foreach (var modifiedDateProviders in _sitemapSourceModifiedDateProviders) { - foreach (var modifiedDateProviders in _sitemapSourceModifiedDateProviders) + var result = await modifiedDateProviders.GetLastModifiedDateAsync(source); + if (result.HasValue && (lastModifiedDate == null || result.Value > lastModifiedDate)) { - var result = await modifiedDateProviders.GetLastModifiedDateAsync(source); - if (result.HasValue && (lastModifiedDate == null || result.Value > lastModifiedDate)) - { - lastModifiedDate = result; - } + lastModifiedDate = result; } } - - return lastModifiedDate; } + + return lastModifiedDate; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapIndexTypeBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapIndexTypeBuilder.cs index 62ef910321a..4e2d84e0ff3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapIndexTypeBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapIndexTypeBuilder.cs @@ -8,71 +8,70 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class SitemapIndexTypeBuilder : SitemapTypeBuilderBase { - public class SitemapIndexTypeBuilder : SitemapTypeBuilderBase + private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; + private static readonly XNamespace _schemaInstance = "http://www.w3.org/2001/XMLSchema-instance"; + private static readonly XNamespace _schemaLocation = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"; + + private readonly ISitemapManager _sitemapManager; + private readonly ISitemapModifiedDateProvider _sitemapModifiedDateProvider; + private readonly SitemapsOptions _sitemapsOptions; + + public SitemapIndexTypeBuilder( + ISitemapManager sitemapManager, + ISitemapModifiedDateProvider sitemapModifiedDateProvider, + IOptions options + ) + { + _sitemapManager = sitemapManager; + _sitemapModifiedDateProvider = sitemapModifiedDateProvider; + _sitemapsOptions = options.Value; + } + + public override async Task BuildSitemapTypeAsync(SitemapIndex sitemap, SitemapBuilderContext context) { - private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; - private static readonly XNamespace _schemaInstance = "http://www.w3.org/2001/XMLSchema-instance"; - private static readonly XNamespace _schemaLocation = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"; + context.Response = new SitemapResponse + { + ResponseElement = new XElement(_namespace + "sitemapindex", + new XAttribute(XNamespace.Xmlns + "xsi", _schemaInstance), + new XAttribute(_schemaInstance + "schemaLocation", _schemaLocation)) + }; - private readonly ISitemapManager _sitemapManager; - private readonly ISitemapModifiedDateProvider _sitemapModifiedDateProvider; - private readonly SitemapsOptions _sitemapsOptions; + var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; - public SitemapIndexTypeBuilder( - ISitemapManager sitemapManager, - ISitemapModifiedDateProvider sitemapModifiedDateProvider, - IOptions options - ) + if (indexSource == null) { - _sitemapManager = sitemapManager; - _sitemapModifiedDateProvider = sitemapModifiedDateProvider; - _sitemapsOptions = options.Value; + return; } - public override async Task BuildSitemapTypeAsync(SitemapIndex sitemap, SitemapBuilderContext context) + var containedSitemaps = (await _sitemapManager.GetSitemapsAsync()) + .Where(s => s.Enabled && indexSource.ContainedSitemapIds.Any(id => id == s.SitemapId)); + + foreach (var containedSitemap in containedSitemaps) { - context.Response = new SitemapResponse + var xmlSitemap = new XElement(_namespace + "sitemap"); + var loc = new XElement(_namespace + "loc"); + + var routeValues = new RouteValueDictionary(_sitemapsOptions.GlobalRouteValues) { - ResponseElement = new XElement(_namespace + "sitemapindex", - new XAttribute(XNamespace.Xmlns + "xsi", _schemaInstance), - new XAttribute(_schemaInstance + "schemaLocation", _schemaLocation)) + [_sitemapsOptions.SitemapIdKey] = containedSitemap.SitemapId }; - var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; + loc.Add(context.HostPrefix + context.UrlHelper.Action(routeValues["Action"].ToString(), routeValues)); + xmlSitemap.Add(loc); - if (indexSource == null) + var lastModDate = await _sitemapModifiedDateProvider.GetLastModifiedDateAsync(containedSitemap); + if (lastModDate.HasValue) { - return; + var lastMod = new XElement(_namespace + "lastmod"); + lastMod.Add(lastModDate.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); + xmlSitemap.Add(lastMod); } - var containedSitemaps = (await _sitemapManager.GetSitemapsAsync()) - .Where(s => s.Enabled && indexSource.ContainedSitemapIds.Any(id => id == s.SitemapId)); - - foreach (var containedSitemap in containedSitemaps) - { - var xmlSitemap = new XElement(_namespace + "sitemap"); - var loc = new XElement(_namespace + "loc"); - - var routeValues = new RouteValueDictionary(_sitemapsOptions.GlobalRouteValues) - { - [_sitemapsOptions.SitemapIdKey] = containedSitemap.SitemapId - }; - - loc.Add(context.HostPrefix + context.UrlHelper.Action(routeValues["Action"].ToString(), routeValues)); - xmlSitemap.Add(loc); - - var lastModDate = await _sitemapModifiedDateProvider.GetLastModifiedDateAsync(containedSitemap); - if (lastModDate.HasValue) - { - var lastMod = new XElement(_namespace + "lastmod"); - lastMod.Add(lastModDate.GetValueOrDefault().ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)); - xmlSitemap.Add(lastMod); - } - - context.Response.ResponseElement.Add(xmlSitemap); - } + context.Response.ResponseElement.Add(xmlSitemap); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapTypeBuilder.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapTypeBuilder.cs index be820753337..0773a1eb44f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapTypeBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Builders/SitemapTypeBuilder.cs @@ -3,32 +3,31 @@ using System.Xml.Linq; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Builders +namespace OrchardCore.Sitemaps.Builders; + +public class SitemapTypeBuilder : SitemapTypeBuilderBase { - public class SitemapTypeBuilder : SitemapTypeBuilderBase - { - private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; + private static readonly XNamespace _namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"; - private readonly IEnumerable _sitemapSourceBuilders; + private readonly IEnumerable _sitemapSourceBuilders; - public SitemapTypeBuilder(IEnumerable sitemapSourceBuilders) - { - _sitemapSourceBuilders = sitemapSourceBuilders; - } + public SitemapTypeBuilder(IEnumerable sitemapSourceBuilders) + { + _sitemapSourceBuilders = sitemapSourceBuilders; + } - public override async Task BuildSitemapTypeAsync(Sitemap sitemap, SitemapBuilderContext context) + public override async Task BuildSitemapTypeAsync(Sitemap sitemap, SitemapBuilderContext context) + { + context.Response = new SitemapResponse { - context.Response = new SitemapResponse - { - ResponseElement = new XElement(_namespace + "urlset") - }; + ResponseElement = new XElement(_namespace + "urlset") + }; - foreach (var source in sitemap.SitemapSources) + foreach (var source in sitemap.SitemapSources) + { + foreach (var sourceBuilder in _sitemapSourceBuilders) { - foreach (var sourceBuilder in _sitemapSourceBuilders) - { - await sourceBuilder.BuildAsync(source, context); - } + await sourceBuilder.BuildAsync(source, context); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/DefaultSitemapCacheProvider.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/DefaultSitemapCacheProvider.cs index e26321dfd61..69d23cbfdac 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/DefaultSitemapCacheProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/DefaultSitemapCacheProvider.cs @@ -9,93 +9,74 @@ using Microsoft.Extensions.Logging; using OrchardCore.Environment.Shell; -namespace OrchardCore.Sitemaps.Cache +namespace OrchardCore.Sitemaps.Cache; + +public class DefaultSitemapCacheProvider : ISitemapCacheProvider { - public class DefaultSitemapCacheProvider : ISitemapCacheProvider - { - // Use default stream copy buffer size to stay in gen0 garbage collection. - private const int StreamCopyBufferSize = 81920; + // Use default stream copy buffer size to stay in gen0 garbage collection. + private const int StreamCopyBufferSize = 81920; - public const string SitemapCachePath = "sm-cache"; + public const string SitemapCachePath = "sm-cache"; - private readonly PhysicalFileProvider _fileProvider; - private readonly ILogger _logger; + private readonly PhysicalFileProvider _fileProvider; + private readonly ILogger _logger; - public DefaultSitemapCacheProvider( - IWebHostEnvironment webHostEnvironment, - ShellSettings shellSettings, - ILogger logger - ) - { - var path = GetSitemapCachePath(webHostEnvironment, shellSettings, SitemapCachePath); + public DefaultSitemapCacheProvider( + IWebHostEnvironment webHostEnvironment, + ShellSettings shellSettings, + ILogger logger + ) + { + var path = GetSitemapCachePath(webHostEnvironment, shellSettings, SitemapCachePath); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } - _fileProvider = new PhysicalFileProvider(path); + _fileProvider = new PhysicalFileProvider(path); - _logger = logger; - } + _logger = logger; + } - public Task GetCachedSitemapAsync(string cacheFileName) + public Task GetCachedSitemapAsync(string cacheFileName) + { + var fileInfo = _fileProvider.GetFileInfo(cacheFileName); + if (fileInfo.Exists) { - var fileInfo = _fileProvider.GetFileInfo(cacheFileName); - if (fileInfo.Exists) - { - return Task.FromResult(new PhysicalSitemapCacheFileResolver(fileInfo)); - } - - return Task.FromResult(null); + return Task.FromResult(new PhysicalSitemapCacheFileResolver(fileInfo)); } - public async Task SetSitemapCacheAsync(Stream stream, string cacheFileName, CancellationToken cancellationToken) - { - var cachePath = Path.Combine(_fileProvider.Root, cacheFileName); + return Task.FromResult(null); + } - using var fileStream = File.Create(cachePath); - stream.Position = 0; - await stream.CopyToAsync(fileStream, StreamCopyBufferSize, cancellationToken); - } + public async Task SetSitemapCacheAsync(Stream stream, string cacheFileName, CancellationToken cancellationToken) + { + var cachePath = Path.Combine(_fileProvider.Root, cacheFileName); + + using var fileStream = File.Create(cachePath); + stream.Position = 0; + await stream.CopyToAsync(fileStream, StreamCopyBufferSize, cancellationToken); + } - public Task CleanSitemapCacheAsync(IEnumerable validCacheFileNames) + public Task CleanSitemapCacheAsync(IEnumerable validCacheFileNames) + { + var folders = _fileProvider.GetDirectoryContents(string.Empty); + foreach (var fileInfo in folders) { - var folders = _fileProvider.GetDirectoryContents(string.Empty); - foreach (var fileInfo in folders) + if (fileInfo.IsDirectory) { - if (fileInfo.IsDirectory) + // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. + continue; + } + else + { + // Check if the file is valid and still needs to be cached. + if (validCacheFileNames.Contains(fileInfo.Name, StringComparer.OrdinalIgnoreCase)) { - // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. continue; } - else - { - // Check if the file is valid and still needs to be cached. - if (validCacheFileNames.Contains(fileInfo.Name, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - - try - { - File.Delete(fileInfo.PhysicalPath); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); - } - } - } - return Task.CompletedTask; - } - - public Task ClearSitemapCacheAsync(string cacheFileName) - { - var fileInfo = _fileProvider.GetFileInfo(cacheFileName); - if (fileInfo.Exists) - { try { File.Delete(fileInfo.PhysicalPath); @@ -105,43 +86,41 @@ public Task ClearSitemapCacheAsync(string cacheFileName) _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); } } - - return Task.CompletedTask; } - public Task PurgeAllAsync() + return Task.CompletedTask; + } + + public Task ClearSitemapCacheAsync(string cacheFileName) + { + var fileInfo = _fileProvider.GetFileInfo(cacheFileName); + if (fileInfo.Exists) { - var hasErrors = false; - var folders = _fileProvider.GetDirectoryContents(string.Empty); - foreach (var fileInfo in folders) + try { - if (fileInfo.IsDirectory) - { - // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. - continue; - } - else - { - try - { - File.Delete(fileInfo.PhysicalPath); - } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); - hasErrors = true; - } - } + File.Delete(fileInfo.PhysicalPath); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); } - - return Task.FromResult(hasErrors); } - public Task PurgeAsync(string cacheFileName) + return Task.CompletedTask; + } + + public Task PurgeAllAsync() + { + var hasErrors = false; + var folders = _fileProvider.GetDirectoryContents(string.Empty); + foreach (var fileInfo in folders) { - var failed = false; - var fileInfo = _fileProvider.GetFileInfo(cacheFileName); - if (fileInfo.Exists) + if (fileInfo.IsDirectory) + { + // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. + continue; + } + else { try { @@ -150,41 +129,61 @@ public Task PurgeAsync(string cacheFileName) catch (IOException ex) { _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); - failed = true; + hasErrors = true; } } - else + } + + return Task.FromResult(hasErrors); + } + + public Task PurgeAsync(string cacheFileName) + { + var failed = false; + var fileInfo = _fileProvider.GetFileInfo(cacheFileName); + if (fileInfo.Exists) + { + try { - _logger.LogError("Cache file {Name} does not exist", cacheFileName); + File.Delete(fileInfo.PhysicalPath); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting cache file {Path}", fileInfo.PhysicalPath); failed = true; } - - return Task.FromResult(failed); + } + else + { + _logger.LogError("Cache file {Name} does not exist", cacheFileName); + failed = true; } - public Task> ListAsync() + return Task.FromResult(failed); + } + + public Task> ListAsync() + { + var results = new List(); + var folders = _fileProvider.GetDirectoryContents(string.Empty); + foreach (var fileInfo in folders) { - var results = new List(); - var folders = _fileProvider.GetDirectoryContents(string.Empty); - foreach (var fileInfo in folders) + if (fileInfo.IsDirectory) { - if (fileInfo.IsDirectory) - { - // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. - continue; - } - else - { - results.Add(fileInfo.Name); - } + // Sitemap cache only stores files, so any folder that has been created by the user will be ignored. + continue; + } + else + { + results.Add(fileInfo.Name); } - - return Task.FromResult>(results); } - private static string GetSitemapCachePath(IWebHostEnvironment webHostEnvironment, ShellSettings shellSettings, string cachePath) - { - return PathExtensions.Combine(webHostEnvironment.WebRootPath, shellSettings.Name, cachePath); - } + return Task.FromResult>(results); + } + + private static string GetSitemapCachePath(IWebHostEnvironment webHostEnvironment, ShellSettings shellSettings, string cachePath) + { + return PathExtensions.Combine(webHostEnvironment.WebRootPath, shellSettings.Name, cachePath); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/PhysicalSitemapCacheFileResolver.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/PhysicalSitemapCacheFileResolver.cs index ae1ebd71e72..d3d5d2b523f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/PhysicalSitemapCacheFileResolver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/PhysicalSitemapCacheFileResolver.cs @@ -2,20 +2,19 @@ using System.Threading.Tasks; using Microsoft.Extensions.FileProviders; -namespace OrchardCore.Sitemaps.Cache +namespace OrchardCore.Sitemaps.Cache; + +public class PhysicalSitemapCacheFileResolver : ISitemapCacheFileResolver { - public class PhysicalSitemapCacheFileResolver : ISitemapCacheFileResolver - { - private readonly IFileInfo _fileInfo; + private readonly IFileInfo _fileInfo; - public PhysicalSitemapCacheFileResolver(IFileInfo fileInfo) - { - _fileInfo = fileInfo; - } + public PhysicalSitemapCacheFileResolver(IFileInfo fileInfo) + { + _fileInfo = fileInfo; + } - public Task OpenReadStreamAsync() - { - return Task.FromResult(_fileInfo.CreateReadStream()); - } + public Task OpenReadStreamAsync() + { + return Task.FromResult(_fileInfo.CreateReadStream()); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/SitemapCacheBackgroundTask.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/SitemapCacheBackgroundTask.cs index 70abf9acc7d..aac9165294c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/SitemapCacheBackgroundTask.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Cache/SitemapCacheBackgroundTask.cs @@ -6,21 +6,20 @@ using OrchardCore.BackgroundTasks; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Cache +namespace OrchardCore.Sitemaps.Cache; + +[BackgroundTask( + Title = "Sitemap Cache Cleaner", + Schedule = "*/5 * * * *", + Description = "Cleans up sitemap cache files.")] +public sealed class SitemapCacheBackgroundTask : IBackgroundTask { - [BackgroundTask( - Title = "Sitemap Cache Cleaner", - Schedule = "*/5 * * * *", - Description = "Cleans up sitemap cache files.")] - public sealed class SitemapCacheBackgroundTask : IBackgroundTask + public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) { - public async Task DoWorkAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) - { - var sitemapManager = serviceProvider.GetRequiredService(); - var sitemapCacheProvider = serviceProvider.GetRequiredService(); + var sitemapManager = serviceProvider.GetRequiredService(); + var sitemapCacheProvider = serviceProvider.GetRequiredService(); - var sitemaps = await sitemapManager.GetSitemapsAsync(); - await sitemapCacheProvider.CleanSitemapCacheAsync(sitemaps.Select(s => s.CacheFileName)); - } + var sitemaps = await sitemapManager.GetSitemapsAsync(); + await sitemapCacheProvider.CleanSitemapCacheAsync(sitemaps.Select(s => s.CacheFileName)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/AdminController.cs index 7f6fd000557..f9065b964c1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/AdminController.cs @@ -19,347 +19,346 @@ using OrchardCore.Sitemaps.Services; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Controllers +namespace OrchardCore.Sitemaps.Controllers; + +[Admin("Sitemaps/{action}/{sitemapId?}", "Sitemaps{action}")] +public class AdminController : Controller { - [Admin("Sitemaps/{action}/{sitemapId?}", "Sitemaps{action}")] - public class AdminController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly ISitemapHelperService _sitemapService; + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _sourceFactories; + private readonly ISitemapManager _sitemapManager; + private readonly ISitemapIdGenerator _sitemapIdGenerator; + private readonly PagerOptions _pagerOptions; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public AdminController( + ISitemapHelperService sitemapService, + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable sourceFactories, + ISitemapManager sitemapManager, + ISitemapIdGenerator sitemapIdGenerator, + IOptions pagerOptions, + IUpdateModelAccessor updateModelAccessor, + INotifier notifier, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) + { + _sitemapService = sitemapService; + _displayManager = displayManager; + _sourceFactories = sourceFactories; + _authorizationService = authorizationService; + _sitemapManager = sitemapManager; + _sitemapIdGenerator = sitemapIdGenerator; + _pagerOptions = pagerOptions.Value; + _updateModelAccessor = updateModelAccessor; + _notifier = notifier; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + } + + public async Task List(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly ISitemapHelperService _sitemapService; - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _sourceFactories; - private readonly ISitemapManager _sitemapManager; - private readonly ISitemapIdGenerator _sitemapIdGenerator; - private readonly PagerOptions _pagerOptions; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public AdminController( - ISitemapHelperService sitemapService, - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable sourceFactories, - ISitemapManager sitemapManager, - ISitemapIdGenerator sitemapIdGenerator, - IOptions pagerOptions, - IUpdateModelAccessor updateModelAccessor, - INotifier notifier, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - _sitemapService = sitemapService; - _displayManager = displayManager; - _sourceFactories = sourceFactories; - _authorizationService = authorizationService; - _sitemapManager = sitemapManager; - _sitemapIdGenerator = sitemapIdGenerator; - _pagerOptions = pagerOptions.Value; - _updateModelAccessor = updateModelAccessor; - _notifier = notifier; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - public async Task List(ContentOptions options, PagerParameters pagerParameters) + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + + var sitemaps = (await _sitemapManager.GetSitemapsAsync()) + .OfType(); + + if (!string.IsNullOrWhiteSpace(options.Search)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + sitemaps = sitemaps.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); + } - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var count = sitemaps.Count(); - var sitemaps = (await _sitemapManager.GetSitemapsAsync()) - .OfType(); + var results = sitemaps + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ToList(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - sitemaps = sitemaps.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); - } + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - var count = sitemaps.Count(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - var results = sitemaps - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); + var model = new ListSitemapViewModel + { + Sitemaps = results.Select(sm => new SitemapListEntry { SitemapId = sm.SitemapId, Name = sm.Name, Enabled = sm.Enabled }).ToList(), + Options = options, + Pager = await _shapeFactory.PagerAsync(pager, count, routeData) + }; - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + return View(model); + } - var model = new ListSitemapViewModel - { - Sitemaps = results.Select(sm => new SitemapListEntry { SitemapId = sm.SitemapId, Name = sm.Name, Enabled = sm.Enabled }).ToList(), - Options = options, - Pager = await _shapeFactory.PagerAsync(pager, count, routeData) - }; + [HttpPost, ActionName(nameof(List))] + [FormValueRequired("submit.Filter")] + public ActionResult ListFilterPOST(ListSitemapViewModel model) + => RedirectToAction(nameof(List), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + public async Task Display(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); + } - return View(model); + var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); + + if (sitemap == null) + { + return NotFound(); } - [HttpPost, ActionName(nameof(List))] - [FormValueRequired("submit.Filter")] - public ActionResult ListFilterPOST(ListSitemapViewModel model) - => RedirectToAction(nameof(List), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + var items = new List(); + foreach (var source in sitemap.SitemapSources) + { + var item = await _displayManager.BuildDisplayAsync(source, _updateModelAccessor.ModelUpdater, "SummaryAdmin"); + item.Properties["SitemapId"] = sitemap.SitemapId; + item.Properties["SitemapSource"] = source; + items.Add(item); + } - public async Task Display(string sitemapId) + var thumbnails = new Dictionary(); + foreach (var factory in _sourceFactories) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + var source = factory.Create(); + var thumbnail = await _displayManager.BuildDisplayAsync(source, _updateModelAccessor.ModelUpdater, "Thumbnail"); + thumbnail.Properties["SitemapSource"] = source; + thumbnail.Properties["SitemapSourceType"] = factory.Name; + thumbnail.Properties["Sitemap"] = sitemap; - var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); + thumbnails.Add(factory.Name, thumbnail); + } - if (sitemap == null) - { - return NotFound(); - } + var model = new DisplaySitemapViewModel + { + Sitemap = sitemap, + Items = items, + Thumbnails = thumbnails, + }; - var items = new List(); - foreach (var source in sitemap.SitemapSources) - { - var item = await _displayManager.BuildDisplayAsync(source, _updateModelAccessor.ModelUpdater, "SummaryAdmin"); - item.Properties["SitemapId"] = sitemap.SitemapId; - item.Properties["SitemapSource"] = source; - items.Add(item); - } + return View(model); + } - var thumbnails = new Dictionary(); - foreach (var factory in _sourceFactories) - { - var source = factory.Create(); - var thumbnail = await _displayManager.BuildDisplayAsync(source, _updateModelAccessor.ModelUpdater, "Thumbnail"); - thumbnail.Properties["SitemapSource"] = source; - thumbnail.Properties["SitemapSourceType"] = factory.Name; - thumbnail.Properties["Sitemap"] = sitemap; + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); + } - thumbnails.Add(factory.Name, thumbnail); - } + var model = new CreateSitemapViewModel(); - var model = new DisplaySitemapViewModel - { - Sitemap = sitemap, - Items = items, - Thumbnails = thumbnails, - }; + return View(model); + } - return View(model); + [HttpPost] + public async Task Create(CreateSitemapViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - public async Task Create() + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + if (string.IsNullOrEmpty(model.Path)) { - return Forbid(); + model.Path = _sitemapService.GetSitemapSlug(model.Name); } - var model = new CreateSitemapViewModel(); - - return View(model); + await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater); } - [HttpPost] - public async Task Create(CreateSitemapViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + var sitemap = new Sitemap { - return Forbid(); - } + SitemapId = _sitemapIdGenerator.GenerateUniqueId(), + Name = model.Name, + Path = model.Path, + Enabled = model.Enabled + }; - if (ModelState.IsValid) - { - if (string.IsNullOrEmpty(model.Path)) - { - model.Path = _sitemapService.GetSitemapSlug(model.Name); - } + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater); - } + return RedirectToAction(nameof(List)); + } - if (ModelState.IsValid) - { - var sitemap = new Sitemap - { - SitemapId = _sitemapIdGenerator.GenerateUniqueId(), - Name = model.Name, - Path = model.Path, - Enabled = model.Enabled - }; + // If we got this far, something failed, redisplay form + return View(model); + } - await _sitemapManager.UpdateSitemapAsync(sitemap); + public async Task Edit(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); + } - return RedirectToAction(nameof(List)); - } + var sitemap = (await _sitemapManager.GetSitemapAsync(sitemapId)) as Sitemap; - // If we got this far, something failed, redisplay form - return View(model); + if (sitemap == null) + { + return NotFound(); } - public async Task Edit(string sitemapId) + var model = new EditSitemapViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + SitemapId = sitemap.SitemapId, + Name = sitemap.Name, + Enabled = sitemap.Enabled, + Path = sitemap.Path + }; - var sitemap = (await _sitemapManager.GetSitemapAsync(sitemapId)) as Sitemap; + return View(model); + } - if (sitemap == null) - { - return NotFound(); - } + [HttpPost] + public async Task Edit(EditSitemapViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); + } - var model = new EditSitemapViewModel - { - SitemapId = sitemap.SitemapId, - Name = sitemap.Name, - Enabled = sitemap.Enabled, - Path = sitemap.Path - }; + var sitemap = (await _sitemapManager.LoadSitemapAsync(model.SitemapId)) as Sitemap; - return View(model); + if (sitemap == null) + { + return NotFound(); } - [HttpPost] - public async Task Edit(EditSitemapViewModel model) + if (ModelState.IsValid) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } - - var sitemap = (await _sitemapManager.LoadSitemapAsync(model.SitemapId)) as Sitemap; - - if (sitemap == null) + if (string.IsNullOrEmpty(model.Path)) { - return NotFound(); + model.Path = _sitemapService.GetSitemapSlug(model.Name); } - if (ModelState.IsValid) - { - if (string.IsNullOrEmpty(model.Path)) - { - model.Path = _sitemapService.GetSitemapSlug(model.Name); - } + await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater, model.SitemapId); + } - await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater, model.SitemapId); - } + if (ModelState.IsValid) + { + sitemap.Name = model.Name; + sitemap.Enabled = model.Enabled; + sitemap.Path = model.Path; - if (ModelState.IsValid) - { - sitemap.Name = model.Name; - sitemap.Enabled = model.Enabled; - sitemap.Path = model.Path; + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _sitemapManager.UpdateSitemapAsync(sitemap); + await _notifier.SuccessAsync(H["Sitemap updated successfully."]); - await _notifier.SuccessAsync(H["Sitemap updated successfully."]); + return RedirectToAction(nameof(List)); + } - return RedirectToAction(nameof(List)); - } + // If we got this far, something failed, redisplay form + return View(model); + } - // If we got this far, something failed, redisplay form - return View(model); + [HttpPost] + public async Task Delete(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string sitemapId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); - var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); + if (sitemap == null) + { + return NotFound(); + } - if (sitemap == null) - { - return NotFound(); - } + await _sitemapManager.DeleteSitemapAsync(sitemapId); - await _sitemapManager.DeleteSitemapAsync(sitemapId); + await _notifier.SuccessAsync(H["Sitemap deleted successfully."]); - await _notifier.SuccessAsync(H["Sitemap deleted successfully."]); + return RedirectToAction(nameof(List)); + } - return RedirectToAction(nameof(List)); + [HttpPost] + public async Task Toggle(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - [HttpPost] - public async Task Toggle(string sitemapId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); - var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); + if (sitemap == null) + { + return NotFound(); + } - if (sitemap == null) - { - return NotFound(); - } + sitemap.Enabled = !sitemap.Enabled; - sitemap.Enabled = !sitemap.Enabled; + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _sitemapManager.UpdateSitemapAsync(sitemap); + await _notifier.SuccessAsync(H["Sitemap toggled successfully."]); - await _notifier.SuccessAsync(H["Sitemap toggled successfully."]); + return RedirectToAction(nameof(List)); + } - return RedirectToAction(nameof(List)); + [HttpPost, ActionName("List")] + [FormValueRequired("submit.BulkAction")] + public async Task ListPost(ViewModels.ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - [HttpPost, ActionName("List")] - [FormValueRequired("submit.BulkAction")] - public async Task ListPost(ViewModels.ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } - - if (itemIds?.Count() > 0) + var sitemapsList = await _sitemapManager.LoadSitemapsAsync(); + var checkedContentItems = sitemapsList.Where(x => itemIds.Contains(x.SitemapId)); + switch (options.BulkAction) { - var sitemapsList = await _sitemapManager.LoadSitemapsAsync(); - var checkedContentItems = sitemapsList.Where(x => itemIds.Contains(x.SitemapId)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _sitemapManager.DeleteSitemapAsync(item.SitemapId); - } - await _notifier.SuccessAsync(H["Sitemaps successfully removed."]); - break; - default: - return BadRequest(); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _sitemapManager.DeleteSitemapAsync(item.SitemapId); + } + await _notifier.SuccessAsync(H["Sitemaps successfully removed."]); + break; + default: + return BadRequest(); } - - return RedirectToAction(nameof(List)); } + + return RedirectToAction(nameof(List)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapCacheController.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapCacheController.cs index 9f364545d89..350828ec518 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapCacheController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapCacheController.cs @@ -8,84 +8,83 @@ using OrchardCore.Sitemaps.Cache; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Controllers +namespace OrchardCore.Sitemaps.Controllers; + +[Admin("SitemapsCache/{action}/{cacheFileName?}", "SitemapsCache{action}")] +public class SitemapCacheController : Controller { - [Admin("SitemapsCache/{action}/{cacheFileName?}", "SitemapsCache{action}")] - public class SitemapCacheController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly ISitemapCacheProvider _sitemapCacheProvider; + private readonly INotifier _notifier; + protected readonly IHtmlLocalizer H; + + public SitemapCacheController( + IAuthorizationService authorizationService, + ISitemapCacheProvider sitemapCacheProvider, + INotifier notifier, + IHtmlLocalizer htmlLocalizer + ) { - private readonly IAuthorizationService _authorizationService; - private readonly ISitemapCacheProvider _sitemapCacheProvider; - private readonly INotifier _notifier; - protected readonly IHtmlLocalizer H; + _authorizationService = authorizationService; + _sitemapCacheProvider = sitemapCacheProvider; + _notifier = notifier; + H = htmlLocalizer; + } - public SitemapCacheController( - IAuthorizationService authorizationService, - ISitemapCacheProvider sitemapCacheProvider, - INotifier notifier, - IHtmlLocalizer htmlLocalizer - ) + public async Task List() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - _authorizationService = authorizationService; - _sitemapCacheProvider = sitemapCacheProvider; - _notifier = notifier; - H = htmlLocalizer; + return Forbid(); } - public async Task List() + var model = new ListSitemapCacheViewModel { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + CachedFileNames = (await _sitemapCacheProvider.ListAsync()).ToArray() + }; - var model = new ListSitemapCacheViewModel - { - CachedFileNames = (await _sitemapCacheProvider.ListAsync()).ToArray() - }; + return View(model); + } - return View(model); + [HttpPost] + public async Task PurgeAll() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - [HttpPost] - public async Task PurgeAll() + var hasErrors = await _sitemapCacheProvider.PurgeAllAsync(); + if (hasErrors) + { + await _notifier.ErrorAsync(H["Sitemap cache purged, with errors."]); + } + else { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + await _notifier.InformationAsync(H["Sitemap cache purged."]); + } - var hasErrors = await _sitemapCacheProvider.PurgeAllAsync(); - if (hasErrors) - { - await _notifier.ErrorAsync(H["Sitemap cache purged, with errors."]); - } - else - { - await _notifier.InformationAsync(H["Sitemap cache purged."]); - } + return RedirectToAction(nameof(List)); + } - return RedirectToAction(nameof(List)); + [HttpPost] + public async Task Purge(string cacheFileName) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); } - [HttpPost] - public async Task Purge(string cacheFileName) + var failed = await _sitemapCacheProvider.PurgeAsync(cacheFileName); + if (failed) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } - - var failed = await _sitemapCacheProvider.PurgeAsync(cacheFileName); - if (failed) - { - await _notifier.ErrorAsync(H["Error purging sitemap cache item."]); - } - else - { - await _notifier.InformationAsync(H["Sitemap cache item purged."]); - } - - return RedirectToAction(nameof(List)); + await _notifier.ErrorAsync(H["Error purging sitemap cache item."]); + } + else + { + await _notifier.InformationAsync(H["Sitemap cache item purged."]); } + + return RedirectToAction(nameof(List)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapController.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapController.cs index c0159ded7c3..8788b256371 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapController.cs @@ -12,116 +12,115 @@ using OrchardCore.Sitemaps.Cache; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Controllers +namespace OrchardCore.Sitemaps.Controllers; + +public class SitemapController : Controller { - public class SitemapController : Controller + private const int WarningLength = 47_185_920; + private const int ErrorLength = 52_428_800; + + private static readonly ConcurrentDictionary>> _workers = new(StringComparer.OrdinalIgnoreCase); + + private readonly ISitemapManager _sitemapManager; + private readonly ISiteService _siteService; + private readonly ISitemapBuilder _sitemapBuilder; + private readonly ISitemapCacheProvider _sitemapCacheProvider; + private readonly string _tenantName; + private readonly ILogger _logger; + + public SitemapController( + ISitemapManager sitemapManager, + ISiteService siteService, + ISitemapBuilder sitemapBuilder, + ISitemapCacheProvider sitemapCacheProvider, + ShellSettings shellSettings, + ILogger logger + ) + { + _sitemapManager = sitemapManager; + _siteService = siteService; + _sitemapBuilder = sitemapBuilder; + _sitemapCacheProvider = sitemapCacheProvider; + _tenantName = shellSettings.Name; + _logger = logger; + } + + public async Task Index(string sitemapId, CancellationToken cancellationToken) { - private const int WarningLength = 47_185_920; - private const int ErrorLength = 52_428_800; - - private static readonly ConcurrentDictionary>> _workers = new(StringComparer.OrdinalIgnoreCase); - - private readonly ISitemapManager _sitemapManager; - private readonly ISiteService _siteService; - private readonly ISitemapBuilder _sitemapBuilder; - private readonly ISitemapCacheProvider _sitemapCacheProvider; - private readonly string _tenantName; - private readonly ILogger _logger; - - public SitemapController( - ISitemapManager sitemapManager, - ISiteService siteService, - ISitemapBuilder sitemapBuilder, - ISitemapCacheProvider sitemapCacheProvider, - ShellSettings shellSettings, - ILogger logger - ) + var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); + if (sitemap == null || !sitemap.Enabled) { - _sitemapManager = sitemapManager; - _siteService = siteService; - _sitemapBuilder = sitemapBuilder; - _sitemapCacheProvider = sitemapCacheProvider; - _tenantName = shellSettings.Name; - _logger = logger; + return NotFound(); } - public async Task Index(string sitemapId, CancellationToken cancellationToken) + var fileResolver = await _sitemapCacheProvider.GetCachedSitemapAsync(sitemap.CacheFileName); + if (fileResolver != null) { - var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); - if (sitemap == null || !sitemap.Enabled) + // When multiple requests occur for the same sitemap it + // may still be building, so we wait for it to complete. + if (_workers.TryGetValue(_tenantName + sitemap.Path, out var writeTask)) { - return NotFound(); + await writeTask.Value; } - var fileResolver = await _sitemapCacheProvider.GetCachedSitemapAsync(sitemap.CacheFileName); - if (fileResolver != null) + // File result will dispose of stream. + var stream = await fileResolver.OpenReadStreamAsync(); + return File(stream, "application/xml"); + } + else + { + var work = await _workers.GetOrAdd(_tenantName + sitemap.Path, x => new Lazy>(async () => { - // When multiple requests occur for the same sitemap it - // may still be building, so we wait for it to complete. - if (_workers.TryGetValue(_tenantName + sitemap.Path, out var writeTask)) + try { - await writeTask.Value; - } + var siteSettings = await _siteService.GetSiteSettingsAsync(); - // File result will dispose of stream. - var stream = await fileResolver.OpenReadStreamAsync(); - return File(stream, "application/xml"); - } - else - { - var work = await _workers.GetOrAdd(_tenantName + sitemap.Path, x => new Lazy>(async () => - { - try + var context = new SitemapBuilderContext() { - var siteSettings = await _siteService.GetSiteSettingsAsync(); - - var context = new SitemapBuilderContext() - { - HostPrefix = siteSettings.BaseUrl, - UrlHelper = Url - }; + HostPrefix = siteSettings.BaseUrl, + UrlHelper = Url + }; - var document = await _sitemapBuilder.BuildAsync(sitemap, context); + var document = await _sitemapBuilder.BuildAsync(sitemap, context); - if (document == null) - { - return null; - } - - document.Declaration = new XDeclaration("1.0", "utf-8", null); - - var stream = new MemoryStream(); - await document.SaveAsync(stream, SaveOptions.None, cancellationToken); + if (document == null) + { + return null; + } - if (stream.Length >= ErrorLength) - { - _logger.LogError("Sitemap 50MB maximum length limit exceeded"); - } - else if (stream.Length >= WarningLength) - { - _logger.LogWarning("Sitemap nearing 50MB length limit"); - } + document.Declaration = new XDeclaration("1.0", "utf-8", null); - await _sitemapCacheProvider.SetSitemapCacheAsync(stream, sitemap.CacheFileName, cancellationToken); + var stream = new MemoryStream(); + await document.SaveAsync(stream, SaveOptions.None, cancellationToken); - return stream; + if (stream.Length >= ErrorLength) + { + _logger.LogError("Sitemap 50MB maximum length limit exceeded"); } - finally + else if (stream.Length >= WarningLength) { - _workers.TryRemove(_tenantName + sitemap.Path, out var writeCacheTask); + _logger.LogWarning("Sitemap nearing 50MB length limit"); } - }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; - if (work == null) + await _sitemapCacheProvider.SetSitemapCacheAsync(stream, sitemap.CacheFileName, cancellationToken); + + return stream; + } + finally { - return NotFound(); + _workers.TryRemove(_tenantName + sitemap.Path, out var writeCacheTask); } + }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; - work.Position = 0; - - // File result will dispose of stream. - return File(work, "application/xml"); + if (work == null) + { + return NotFound(); } + + work.Position = 0; + + // File result will dispose of stream. + return File(work, "application/xml"); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapIndexController.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapIndexController.cs index d381f0532a4..491d6ba95a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapIndexController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SitemapIndexController.cs @@ -19,349 +19,348 @@ using OrchardCore.Sitemaps.Services; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Controllers +namespace OrchardCore.Sitemaps.Controllers; + +[Admin("SitemapIndexes/{action}/{sitemapId?}", "SitemapIndexes{action}")] +public class SitemapIndexController : Controller { - [Admin("SitemapIndexes/{action}/{sitemapId?}", "SitemapIndexes{action}")] - public class SitemapIndexController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly ISitemapHelperService _sitemapService; + private readonly IAuthorizationService _authorizationService; + private readonly ISitemapIdGenerator _sitemapIdGenerator; + private readonly ISitemapManager _sitemapManager; + private readonly PagerOptions _pagerOptions; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly INotifier _notifier; + private readonly IShapeFactory _shapeFactory; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public SitemapIndexController( + ISitemapHelperService sitemapService, + IAuthorizationService authorizationService, + ISitemapIdGenerator sitemapIdGenerator, + ISitemapManager sitemapManager, + IOptions pagerOptions, + IUpdateModelAccessor updateModelAccessor, + IShapeFactory shapeFactory, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier) { - private const string _optionsSearch = "Options.Search"; - - private readonly ISitemapHelperService _sitemapService; - private readonly IAuthorizationService _authorizationService; - private readonly ISitemapIdGenerator _sitemapIdGenerator; - private readonly ISitemapManager _sitemapManager; - private readonly PagerOptions _pagerOptions; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly INotifier _notifier; - private readonly IShapeFactory _shapeFactory; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public SitemapIndexController( - ISitemapHelperService sitemapService, - IAuthorizationService authorizationService, - ISitemapIdGenerator sitemapIdGenerator, - ISitemapManager sitemapManager, - IOptions pagerOptions, - IUpdateModelAccessor updateModelAccessor, - IShapeFactory shapeFactory, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier) - { - _sitemapService = sitemapService; - _authorizationService = authorizationService; - _sitemapIdGenerator = sitemapIdGenerator; - _sitemapManager = sitemapManager; - _pagerOptions = pagerOptions.Value; - _updateModelAccessor = updateModelAccessor; - _notifier = notifier; - _shapeFactory = shapeFactory; - S = stringLocalizer; - H = htmlLocalizer; - } + _sitemapService = sitemapService; + _authorizationService = authorizationService; + _sitemapIdGenerator = sitemapIdGenerator; + _sitemapManager = sitemapManager; + _pagerOptions = pagerOptions.Value; + _updateModelAccessor = updateModelAccessor; + _notifier = notifier; + _shapeFactory = shapeFactory; + S = stringLocalizer; + H = htmlLocalizer; + } - public async Task List(ContentOptions options, PagerParameters pagerParameters) + public async Task List(ContentOptions options, PagerParameters pagerParameters) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var sitemaps = (await _sitemapManager.GetSitemapsAsync()) - .OfType(); + var sitemaps = (await _sitemapManager.GetSitemapsAsync()) + .OfType(); - if (!string.IsNullOrWhiteSpace(options.Search)) - { - sitemaps = sitemaps.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); - } + if (!string.IsNullOrWhiteSpace(options.Search)) + { + sitemaps = sitemaps.Where(x => x.Name.Contains(options.Search, StringComparison.OrdinalIgnoreCase)); + } - var count = sitemaps.Count(); + var count = sitemaps.Count(); - var results = sitemaps - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize) - .ToList(); + var results = sitemaps + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize) + .ToList(); - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var model = new ListSitemapIndexViewModel - { - SitemapIndexes = results.Select(sm => new SitemapIndexListEntry { SitemapId = sm.SitemapId, Name = sm.Name, Enabled = sm.Enabled }).ToList(), - Options = options, - Pager = pagerShape - }; + var model = new ListSitemapIndexViewModel + { + SitemapIndexes = results.Select(sm => new SitemapIndexListEntry { SitemapId = sm.SitemapId, Name = sm.Name, Enabled = sm.Enabled }).ToList(), + Options = options, + Pager = pagerShape + }; - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; - return View(model); - } + return View(model); + } - [HttpPost, ActionName(nameof(List))] - [FormValueRequired("submit.Filter")] - public ActionResult ListFilterPOST(ListSitemapIndexViewModel model) - => RedirectToAction(nameof(List), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + [HttpPost, ActionName(nameof(List))] + [FormValueRequired("submit.Filter")] + public ActionResult ListFilterPOST(ListSitemapIndexViewModel model) + => RedirectToAction(nameof(List), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - public async Task Create() + public async Task Create() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemaps = await _sitemapManager.GetSitemapsAsync(); - - var containableSitemaps = sitemaps - .Where(s => s.GetType() != typeof(SitemapIndex)) - .Select(s => new ContainableSitemapEntryViewModel - { - SitemapId = s.SitemapId, - Name = s.Name, - IsChecked = false - }) - .OrderBy(s => s.Name) - .ToArray(); + var sitemaps = await _sitemapManager.GetSitemapsAsync(); - var model = new CreateSitemapIndexViewModel + var containableSitemaps = sitemaps + .Where(s => s.GetType() != typeof(SitemapIndex)) + .Select(s => new ContainableSitemapEntryViewModel { - ContainableSitemaps = containableSitemaps - }; + SitemapId = s.SitemapId, + Name = s.Name, + IsChecked = false + }) + .OrderBy(s => s.Name) + .ToArray(); + + var model = new CreateSitemapIndexViewModel + { + ContainableSitemaps = containableSitemaps + }; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Create(CreateSitemapIndexViewModel model) + [HttpPost] + public async Task Create(CreateSitemapIndexViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } - - var sitemap = new SitemapIndex - { - SitemapId = _sitemapIdGenerator.GenerateUniqueId() - }; + return Forbid(); + } - var indexSource = new SitemapIndexSource - { - Id = _sitemapIdGenerator.GenerateUniqueId() - }; + var sitemap = new SitemapIndex + { + SitemapId = _sitemapIdGenerator.GenerateUniqueId() + }; - sitemap.SitemapSources.Add(indexSource); + var indexSource = new SitemapIndexSource + { + Id = _sitemapIdGenerator.GenerateUniqueId() + }; - if (ModelState.IsValid) - { - await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater); + sitemap.SitemapSources.Add(indexSource); - } + if (ModelState.IsValid) + { + await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater); - // Path validation may invalidate model state. - if (ModelState.IsValid) - { - sitemap.Name = model.Name; - sitemap.Enabled = model.Enabled; - sitemap.Path = model.Path; + } - indexSource.ContainedSitemapIds = model.ContainableSitemaps - .Where(m => m.IsChecked) - .Select(m => m.SitemapId) - .ToArray(); + // Path validation may invalidate model state. + if (ModelState.IsValid) + { + sitemap.Name = model.Name; + sitemap.Enabled = model.Enabled; + sitemap.Path = model.Path; - await _sitemapManager.UpdateSitemapAsync(sitemap); + indexSource.ContainedSitemapIds = model.ContainableSitemaps + .Where(m => m.IsChecked) + .Select(m => m.SitemapId) + .ToArray(); - await _notifier.SuccessAsync(H["Sitemap index created successfully"]); + await _sitemapManager.UpdateSitemapAsync(sitemap); - return RedirectToAction(nameof(List)); - } + await _notifier.SuccessAsync(H["Sitemap index created successfully"]); - // If we got this far, something failed, redisplay form - return View(model); + return RedirectToAction(nameof(List)); } - public async Task Edit(string sitemapId) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + // If we got this far, something failed, redisplay form + return View(model); + } - var sitemap = (await _sitemapManager.GetSitemapAsync(sitemapId)) as SitemapIndex; + public async Task Edit(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) + { + return Forbid(); + } - if (sitemap == null) - { - return NotFound(); - } + var sitemap = (await _sitemapManager.GetSitemapAsync(sitemapId)) as SitemapIndex; - var sitemaps = await _sitemapManager.GetSitemapsAsync(); + if (sitemap == null) + { + return NotFound(); + } - var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; + var sitemaps = await _sitemapManager.GetSitemapsAsync(); - var containableSitemaps = sitemaps - .Where(s => s.GetType() != typeof(SitemapIndex)) - .Select(s => new ContainableSitemapEntryViewModel - { - SitemapId = s.SitemapId, - Name = s.Name, - IsChecked = indexSource.ContainedSitemapIds.Any(id => id == s.SitemapId) - }) - .OrderBy(s => s.Name) - .ToArray(); + var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; - var model = new EditSitemapIndexViewModel + var containableSitemaps = sitemaps + .Where(s => s.GetType() != typeof(SitemapIndex)) + .Select(s => new ContainableSitemapEntryViewModel { - SitemapId = sitemap.SitemapId, - Name = sitemap.Name, - Enabled = sitemap.Enabled, - Path = sitemap.Path, - SitemapIndexSource = indexSource, - ContainableSitemaps = containableSitemaps - }; - - return View(model); - } + SitemapId = s.SitemapId, + Name = s.Name, + IsChecked = indexSource.ContainedSitemapIds.Any(id => id == s.SitemapId) + }) + .OrderBy(s => s.Name) + .ToArray(); + + var model = new EditSitemapIndexViewModel + { + SitemapId = sitemap.SitemapId, + Name = sitemap.Name, + Enabled = sitemap.Enabled, + Path = sitemap.Path, + SitemapIndexSource = indexSource, + ContainableSitemaps = containableSitemaps + }; + + return View(model); + } - [HttpPost] - public async Task Edit(EditSitemapIndexViewModel model) + [HttpPost] + public async Task Edit(EditSitemapIndexViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; + var indexSource = sitemap.SitemapSources.FirstOrDefault() as SitemapIndexSource; - model.SitemapIndexSource = indexSource; + model.SitemapIndexSource = indexSource; - if (ModelState.IsValid) - { - await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater, sitemap.SitemapId); - } - - // Path validation may invalidate model state. - if (ModelState.IsValid) - { - sitemap.Name = model.Name; - sitemap.Enabled = model.Enabled; - sitemap.Path = model.Path; + if (ModelState.IsValid) + { + await _sitemapService.ValidatePathAsync(model.Path, _updateModelAccessor.ModelUpdater, sitemap.SitemapId); + } - indexSource.ContainedSitemapIds = model.ContainableSitemaps - .Where(m => m.IsChecked) - .Select(m => m.SitemapId) - .ToArray(); + // Path validation may invalidate model state. + if (ModelState.IsValid) + { + sitemap.Name = model.Name; + sitemap.Enabled = model.Enabled; + sitemap.Path = model.Path; - await _sitemapManager.UpdateSitemapAsync(sitemap); + indexSource.ContainedSitemapIds = model.ContainableSitemaps + .Where(m => m.IsChecked) + .Select(m => m.SitemapId) + .ToArray(); - await _notifier.SuccessAsync(H["Sitemap index updated successfully"]); + await _sitemapManager.UpdateSitemapAsync(sitemap); - return RedirectToAction(nameof(List)); - } + await _notifier.SuccessAsync(H["Sitemap index updated successfully"]); - // If we got this far, something failed, redisplay form - return View(model); + return RedirectToAction(nameof(List)); } - [HttpPost] - public async Task Delete(string sitemapId) + // If we got this far, something failed, redisplay form + return View(model); + } + + [HttpPost] + public async Task Delete(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - await _sitemapManager.DeleteSitemapAsync(sitemapId); + await _sitemapManager.DeleteSitemapAsync(sitemapId); - await _notifier.SuccessAsync(H["Sitemap index deleted successfully."]); + await _notifier.SuccessAsync(H["Sitemap index deleted successfully."]); - return RedirectToAction(nameof(List)); - } + return RedirectToAction(nameof(List)); + } - [HttpPost] - public async Task Toggle(string sitemapId) + [HttpPost] + public async Task Toggle(string sitemapId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - sitemap.Enabled = !sitemap.Enabled; + sitemap.Enabled = !sitemap.Enabled; - await _sitemapManager.UpdateSitemapAsync(sitemap); + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _notifier.SuccessAsync(H["Sitemap index menu toggled successfully."]); + await _notifier.SuccessAsync(H["Sitemap index menu toggled successfully."]); - return RedirectToAction(nameof(List)); - } + return RedirectToAction(nameof(List)); + } - [HttpPost, ActionName("List")] - [FormValueRequired("submit.BulkAction")] - public async Task ListPost(ViewModels.ContentOptions options, IEnumerable itemIds) + [HttpPost, ActionName("List")] + [FormValueRequired("submit.BulkAction")] + public async Task ListPost(ViewModels.ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - if (itemIds?.Count() > 0) + if (itemIds?.Count() > 0) + { + var sitemapsList = await _sitemapManager.LoadSitemapsAsync(); + var checkedContentItems = sitemapsList.Where(x => itemIds.Contains(x.SitemapId)); + switch (options.BulkAction) { - var sitemapsList = await _sitemapManager.LoadSitemapsAsync(); - var checkedContentItems = sitemapsList.Where(x => itemIds.Contains(x.SitemapId)); - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await _sitemapManager.DeleteSitemapAsync(item.SitemapId); - } - await _notifier.SuccessAsync(H["Sitemap indices successfully removed."]); - break; - default: - return BadRequest(); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await _sitemapManager.DeleteSitemapAsync(item.SitemapId); + } + await _notifier.SuccessAsync(H["Sitemap indices successfully removed."]); + break; + default: + return BadRequest(); } - - return RedirectToAction(nameof(List)); } + + return RedirectToAction(nameof(List)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SourceController.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SourceController.cs index 89140252c0b..d878da35d16 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SourceController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Controllers/SourceController.cs @@ -14,223 +14,222 @@ using OrchardCore.Sitemaps.Services; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Controllers +namespace OrchardCore.Sitemaps.Controllers; + +[Admin] +public class SourceController : Controller { - [Admin] - public class SourceController : Controller + private readonly IAuthorizationService _authorizationService; + private readonly IDisplayManager _displayManager; + private readonly IEnumerable _factories; + private readonly ISitemapManager _sitemapManager; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly INotifier _notifier; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public SourceController( + IAuthorizationService authorizationService, + IDisplayManager displayManager, + IEnumerable factories, + ISitemapManager sitemapManager, + IUpdateModelAccessor updateModelAccessor, + INotifier notifier, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer) { - private readonly IAuthorizationService _authorizationService; - private readonly IDisplayManager _displayManager; - private readonly IEnumerable _factories; - private readonly ISitemapManager _sitemapManager; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly INotifier _notifier; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; + _displayManager = displayManager; + _factories = factories; + _authorizationService = authorizationService; + _sitemapManager = sitemapManager; + _updateModelAccessor = updateModelAccessor; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + } - public SourceController( - IAuthorizationService authorizationService, - IDisplayManager displayManager, - IEnumerable factories, - ISitemapManager sitemapManager, - IUpdateModelAccessor updateModelAccessor, - INotifier notifier, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer) + [Admin("SitemapSource/Create/{sitemapId}/{sourceType}", "SitemapsSourceCreate")] + public async Task Create(string sitemapId, string sourceType) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - _displayManager = displayManager; - _factories = factories; - _authorizationService = authorizationService; - _sitemapManager = sitemapManager; - _updateModelAccessor = updateModelAccessor; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - [Admin("SitemapSource/Create/{sitemapId}/{sourceType}", "SitemapsSourceCreate")] - public async Task Create(string sitemapId, string sourceType) - { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } - - var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); + var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var source = _factories.FirstOrDefault(x => x.Name == sourceType)?.Create(); + var source = _factories.FirstOrDefault(x => x.Name == sourceType)?.Create(); - if (source == null) - { - return NotFound(); - } + if (source == null) + { + return NotFound(); + } - var model = new CreateSourceViewModel - { - SitemapId = sitemapId, - SitemapSource = source, - SitemapSourceId = source.Id, - SitemapSourceType = sourceType, - Editor = await _displayManager.BuildEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", "") - }; + var model = new CreateSourceViewModel + { + SitemapId = sitemapId, + SitemapSource = source, + SitemapSourceId = source.Id, + SitemapSourceType = sourceType, + Editor = await _displayManager.BuildEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", "") + }; - model.Editor.SitemapSource = source; + model.Editor.SitemapSource = source; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Create(CreateSourceViewModel model) + [HttpPost] + public async Task Create(CreateSourceViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var source = _factories.FirstOrDefault(x => x.Name == model.SitemapSourceType)?.Create(); + var source = _factories.FirstOrDefault(x => x.Name == model.SitemapSourceType)?.Create(); - if (source == null) - { - return NotFound(); - } + if (source == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); - editor.Properties["SitemapStep"] = source; + var editor = await _displayManager.UpdateEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: true, "", ""); + editor.Properties["SitemapStep"] = source; - if (ModelState.IsValid) - { - source.Id = model.SitemapSourceId; - sitemap.SitemapSources.Add(source); + if (ModelState.IsValid) + { + source.Id = model.SitemapSourceId; + sitemap.SitemapSources.Add(source); - await _sitemapManager.UpdateSitemapAsync(sitemap); + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _notifier.SuccessAsync(H["Sitemap source added successfully."]); - return RedirectToAction("Display", "Admin", new { sitemapId = model.SitemapId }); - } + await _notifier.SuccessAsync(H["Sitemap source added successfully."]); + return RedirectToAction("Display", "Admin", new { sitemapId = model.SitemapId }); + } - model.Editor = editor; + model.Editor = editor; - // If we got this far, something failed, redisplay form - return View(model); - } + // If we got this far, something failed, redisplay form + return View(model); + } - [Admin("SitemapSource/Edit/{sitemapId}/{sourceId}", "SitemapsSourceEdit")] - public async Task Edit(string sitemapId, string sourceId) + [Admin("SitemapSource/Edit/{sitemapId}/{sourceId}", "SitemapsSourceEdit")] + public async Task Edit(string sitemapId, string sourceId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); + var sitemap = await _sitemapManager.GetSitemapAsync(sitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, sourceId, StringComparison.OrdinalIgnoreCase)); + var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, sourceId, StringComparison.OrdinalIgnoreCase)); - if (source == null) - { - return NotFound(); - } + if (source == null) + { + return NotFound(); + } - var model = new EditSourceViewModel - { - SitemapId = sitemapId, - SitemapSource = source, - SitemapSourceId = source.Id, - Editor = await _displayManager.BuildEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") - }; + var model = new EditSourceViewModel + { + SitemapId = sitemapId, + SitemapSource = source, + SitemapSourceId = source.Id, + Editor = await _displayManager.BuildEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", "") + }; - model.Editor.SitemapSource = source; + model.Editor.SitemapSource = source; - return View(model); - } + return View(model); + } - [HttpPost] - public async Task Edit(EditSourceViewModel model) + [HttpPost] + public async Task Edit(EditSourceViewModel model) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(model.SitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, model.SitemapSourceId, StringComparison.OrdinalIgnoreCase)); + var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, model.SitemapSourceId, StringComparison.OrdinalIgnoreCase)); - if (source == null) - { - return NotFound(); - } + if (source == null) + { + return NotFound(); + } - var editor = await _displayManager.UpdateEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); + var editor = await _displayManager.UpdateEditorAsync(source, updater: _updateModelAccessor.ModelUpdater, isNew: false, "", ""); - if (ModelState.IsValid) - { - await _sitemapManager.UpdateSitemapAsync(sitemap); + if (ModelState.IsValid) + { + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _notifier.SuccessAsync(H["Sitemap source updated successfully."]); - return RedirectToAction("Display", "Admin", new { sitemapId = model.SitemapId }); - } + await _notifier.SuccessAsync(H["Sitemap source updated successfully."]); + return RedirectToAction("Display", "Admin", new { sitemapId = model.SitemapId }); + } - await _notifier.ErrorAsync(H["The sitemap source has validation errors."]); - model.Editor = editor; + await _notifier.ErrorAsync(H["The sitemap source has validation errors."]); + model.Editor = editor; - // If we got this far, something failed, redisplay form - return View(model); - } + // If we got this far, something failed, redisplay form + return View(model); + } - [HttpPost] - [Admin("SitemapSource/Delete/{sitemapId}/{sourceId}", "SitemapsSourceDelete")] - public async Task Delete(string sitemapId, string sourceId) + [HttpPost] + [Admin("SitemapSource/Delete/{sitemapId}/{sourceId}", "SitemapsSourceDelete")] + public async Task Delete(string sitemapId, string sourceId) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageSitemaps)) - { - return Forbid(); - } + return Forbid(); + } - var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); + var sitemap = await _sitemapManager.LoadSitemapAsync(sitemapId); - if (sitemap == null) - { - return NotFound(); - } + if (sitemap == null) + { + return NotFound(); + } - var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, sourceId, StringComparison.OrdinalIgnoreCase)); + var source = sitemap.SitemapSources.FirstOrDefault(x => string.Equals(x.Id, sourceId, StringComparison.OrdinalIgnoreCase)); - if (source == null) - { - return NotFound(); - } + if (source == null) + { + return NotFound(); + } - sitemap.SitemapSources.Remove(source); + sitemap.SitemapSources.Remove(source); - await _sitemapManager.UpdateSitemapAsync(sitemap); + await _sitemapManager.UpdateSitemapAsync(sitemap); - await _notifier.SuccessAsync(H["Sitemap source deleted successfully."]); + await _notifier.SuccessAsync(H["Sitemap source deleted successfully."]); - return RedirectToAction("Display", "Admin", new { sitemapId }); - } + return RedirectToAction("Display", "Admin", new { sitemapId }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentSource.cs index 67a817cba33..7ad943c3231 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentSource.cs @@ -5,37 +5,36 @@ using OrchardCore.Json; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Deployment +namespace OrchardCore.Sitemaps.Deployment; + +public sealed class AllSitemapsDeploymentSource : IDeploymentSource { - public sealed class AllSitemapsDeploymentSource : IDeploymentSource + private readonly ISitemapManager _sitemapManager; + private readonly DocumentJsonSerializerOptions _documentJsonSerializerOptions; + + public AllSitemapsDeploymentSource( + ISitemapManager sitemapManager, + IOptions documentJsonSerializerOptions) { - private readonly ISitemapManager _sitemapManager; - private readonly DocumentJsonSerializerOptions _documentJsonSerializerOptions; + _sitemapManager = sitemapManager; + _documentJsonSerializerOptions = documentJsonSerializerOptions.Value; + } - public AllSitemapsDeploymentSource( - ISitemapManager sitemapManager, - IOptions documentJsonSerializerOptions) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AllSitemapsDeploymentStep) { - _sitemapManager = sitemapManager; - _documentJsonSerializerOptions = documentJsonSerializerOptions.Value; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not AllSitemapsDeploymentStep) - { - return; - } - - var sitemaps = await _sitemapManager.GetSitemapsAsync(); + var sitemaps = await _sitemapManager.GetSitemapsAsync(); - var jArray = JArray.FromObject(sitemaps, _documentJsonSerializerOptions.SerializerOptions); + var jArray = JArray.FromObject(sitemaps, _documentJsonSerializerOptions.SerializerOptions); - result.Steps.Add(new JsonObject - { - ["name"] = "Sitemaps", - ["data"] = jArray, - }); - } + result.Steps.Add(new JsonObject + { + ["name"] = "Sitemaps", + ["data"] = jArray, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStep.cs index 3b408d8a00b..53d4aed7a5c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStep.cs @@ -1,12 +1,11 @@ using OrchardCore.Deployment; -namespace OrchardCore.Sitemaps.Deployment +namespace OrchardCore.Sitemaps.Deployment; + +public sealed class AllSitemapsDeploymentStep : DeploymentStep { - public sealed class AllSitemapsDeploymentStep : DeploymentStep + public AllSitemapsDeploymentStep() { - public AllSitemapsDeploymentStep() - { - Name = "AllSitemaps"; - } + Name = "AllSitemaps"; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStepDriver.cs index 3a34244e9f0..94b121597cd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Deployment/AllSitemapsDeploymentStepDriver.cs @@ -3,22 +3,21 @@ using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.Views; -namespace OrchardCore.Sitemaps.Deployment +namespace OrchardCore.Sitemaps.Deployment; + +public sealed class AllSitemapsDeploymentStepDriver : DisplayDriver { - public sealed class AllSitemapsDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllSitemapsDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllSitemapsDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllSitemapsDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllSitemapsDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllSitemapsDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllSitemapsDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllSitemapsDeploymentStep step, BuildEditorContext context) - { - return View("AllSitemapsDeploymentStep_Edit", step).Location("Content"); - } + public override IDisplayResult Edit(AllSitemapsDeploymentStep step, BuildEditorContext context) + { + return View("AllSitemapsDeploymentStep_Edit", step).Location("Content"); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/CustomPathSitemapSourceDriver.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/CustomPathSitemapSourceDriver.cs index 31826e48d77..91bd4b2e385 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/CustomPathSitemapSourceDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/CustomPathSitemapSourceDriver.cs @@ -8,64 +8,63 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Drivers +namespace OrchardCore.Sitemaps.Drivers; + +public sealed class CustomPathSitemapSourceDriver : DisplayDriver { - public sealed class CustomPathSitemapSourceDriver : DisplayDriver - { - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public CustomPathSitemapSourceDriver(IStringLocalizer localizer) - { - S = localizer; - } + public CustomPathSitemapSourceDriver(IStringLocalizer localizer) + { + S = localizer; + } - public override Task DisplayAsync(CustomPathSitemapSource sitemapSource, BuildDisplayContext context) - { - return CombineAsync( - View("CustomPathSitemapSource_SummaryAdmin", sitemapSource).Location("SummaryAdmin", "Content"), - View("CustomPathSitemapSource_Thumbnail", sitemapSource).Location("Thumbnail", "Content") - ); - } + public override Task DisplayAsync(CustomPathSitemapSource sitemapSource, BuildDisplayContext context) + { + return CombineAsync( + View("CustomPathSitemapSource_SummaryAdmin", sitemapSource).Location("SummaryAdmin", "Content"), + View("CustomPathSitemapSource_Thumbnail", sitemapSource).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(CustomPathSitemapSource sitemapSource, BuildEditorContext context) + public override IDisplayResult Edit(CustomPathSitemapSource sitemapSource, BuildEditorContext context) + { + return Initialize("CustomPathSitemapSource_Edit", model => { - return Initialize("CustomPathSitemapSource_Edit", model => - { - model.Path = sitemapSource.Path; - model.Priority = sitemapSource.Priority; - model.ChangeFrequency = sitemapSource.ChangeFrequency; - - }).Location("Content"); - } + model.Path = sitemapSource.Path; + model.Priority = sitemapSource.Priority; + model.ChangeFrequency = sitemapSource.ChangeFrequency; - public override async Task UpdateAsync(CustomPathSitemapSource sitemap, UpdateEditorContext context) - { - var model = new CustomPathSitemapSourceViewModel(); + }).Location("Content"); + } - await context.Updater.TryUpdateModelAsync(model, - Prefix, - m => m.Path, - m => m.Priority, - m => m.ChangeFrequency - ); + public override async Task UpdateAsync(CustomPathSitemapSource sitemap, UpdateEditorContext context) + { + var model = new CustomPathSitemapSourceViewModel(); - sitemap.Path = model.Path; - sitemap.Priority = model.Priority; - sitemap.ChangeFrequency = model.ChangeFrequency; - sitemap.LastUpdate = DateTime.Now; + await context.Updater.TryUpdateModelAsync(model, + Prefix, + m => m.Path, + m => m.Priority, + m => m.ChangeFrequency + ); - if (sitemap.Path?.IndexOfAny(CustomPathSitemapSource.InvalidCharactersForPath) > -1 || sitemap.Path?.IndexOf(' ') > -1 || sitemap.Path?.IndexOf("//") > -1) - { - var invalidCharactersForMessage = string.Join(", ", CustomPathSitemapSource.InvalidCharactersForPath.Select(c => $"\"{c}\"")); - context.Updater.ModelState.AddModelError(Prefix, sitemap.Path, S["Please do not use any of the following characters in your permalink: {0}. No spaces, or consecutive slashes, are allowed (please use dashes or underscores instead).", invalidCharactersForMessage]); - } + sitemap.Path = model.Path; + sitemap.Priority = model.Priority; + sitemap.ChangeFrequency = model.ChangeFrequency; + sitemap.LastUpdate = DateTime.Now; - if (sitemap.Path?.Length > CustomPathSitemapSource.MaxPathLength) - { - context.Updater.ModelState.AddModelError(Prefix, sitemap.Path, S["Your path is too long. The path can only be up to {0} characters.", CustomPathSitemapSource.MaxPathLength]); - } + if (sitemap.Path?.IndexOfAny(CustomPathSitemapSource.InvalidCharactersForPath) > -1 || sitemap.Path?.IndexOf(' ') > -1 || sitemap.Path?.IndexOf("//") > -1) + { + var invalidCharactersForMessage = string.Join(", ", CustomPathSitemapSource.InvalidCharactersForPath.Select(c => $"\"{c}\"")); + context.Updater.ModelState.AddModelError(Prefix, sitemap.Path, S["Please do not use any of the following characters in your permalink: {0}. No spaces, or consecutive slashes, are allowed (please use dashes or underscores instead).", invalidCharactersForMessage]); + } - return Edit(sitemap, context); + if (sitemap.Path?.Length > CustomPathSitemapSource.MaxPathLength) + { + context.Updater.ModelState.AddModelError(Prefix, sitemap.Path, S["Your path is too long. The path can only be up to {0} characters.", CustomPathSitemapSource.MaxPathLength]); } + + return Edit(sitemap, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/SitemapPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/SitemapPartDisplayDriver.cs index 57800b497b3..c17e4a8af03 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/SitemapPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Drivers/SitemapPartDisplayDriver.cs @@ -6,43 +6,42 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.ViewModels; -namespace OrchardCore.Sitemaps.Drivers +namespace OrchardCore.Sitemaps.Drivers; + +public sealed class SitemapPartDisplayDriver : ContentPartDisplayDriver { - public sealed class SitemapPartDisplayDriver : ContentPartDisplayDriver + public override IDisplayResult Edit(SitemapPart part, BuildPartEditorContext context) + { + return Initialize("SitemapPart_Edit", m => BuildViewModel(m, part)) + .Location("Parts#SEO:5"); + } + + public override async Task UpdateAsync(SitemapPart model, UpdatePartEditorContext context) + { + var viewModel = new SitemapPartViewModel(); + + await context.Updater.TryUpdateModelAsync(viewModel, + Prefix, + t => t.OverrideSitemapConfig, + t => t.ChangeFrequency, + t => t.Exclude, + t => t.Priority); + + model.OverrideSitemapConfig = viewModel.OverrideSitemapConfig; + model.ChangeFrequency = viewModel.ChangeFrequency; + model.Exclude = viewModel.Exclude; + model.Priority = viewModel.Priority; + + return Edit(model, context); + } + + + private static void BuildViewModel(SitemapPartViewModel model, SitemapPart part) { - public override IDisplayResult Edit(SitemapPart part, BuildPartEditorContext context) - { - return Initialize("SitemapPart_Edit", m => BuildViewModel(m, part)) - .Location("Parts#SEO:5"); - } - - public override async Task UpdateAsync(SitemapPart model, UpdatePartEditorContext context) - { - var viewModel = new SitemapPartViewModel(); - - await context.Updater.TryUpdateModelAsync(viewModel, - Prefix, - t => t.OverrideSitemapConfig, - t => t.ChangeFrequency, - t => t.Exclude, - t => t.Priority); - - model.OverrideSitemapConfig = viewModel.OverrideSitemapConfig; - model.ChangeFrequency = viewModel.ChangeFrequency; - model.Exclude = viewModel.Exclude; - model.Priority = viewModel.Priority; - - return Edit(model, context); - } - - - private static void BuildViewModel(SitemapPartViewModel model, SitemapPart part) - { - model.OverrideSitemapConfig = part.OverrideSitemapConfig; - model.ChangeFrequency = part.ChangeFrequency; - model.Exclude = part.Exclude; - model.Priority = part.Priority; - model.SitemapPart = part; - } + model.OverrideSitemapConfig = part.OverrideSitemapConfig; + model.ChangeFrequency = part.ChangeFrequency; + model.Exclude = part.Exclude; + model.Priority = part.Priority; + model.SitemapPart = part; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/CustomPathSitemapSourceUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/CustomPathSitemapSourceUpdateHandler.cs index f3155ed4502..23277e2d250 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/CustomPathSitemapSourceUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/CustomPathSitemapSourceUpdateHandler.cs @@ -3,42 +3,41 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Handlers +namespace OrchardCore.Sitemaps.Handlers; + +public class CustomPathSitemapSourceUpdateHandler : ISitemapSourceUpdateHandler { - public class CustomPathSitemapSourceUpdateHandler : ISitemapSourceUpdateHandler + private readonly ISitemapManager _sitemapManager; + + public CustomPathSitemapSourceUpdateHandler(ISitemapManager sitemapManager) { - private readonly ISitemapManager _sitemapManager; + _sitemapManager = sitemapManager; + } - public CustomPathSitemapSourceUpdateHandler(ISitemapManager sitemapManager) + public async Task UpdateSitemapAsync(SitemapUpdateContext context) + { + var sitemaps = (await _sitemapManager.LoadSitemapsAsync()).Where(s => s.GetType() == typeof(Sitemap)); + + if (!sitemaps.Any()) { - _sitemapManager = sitemapManager; + return; } - public async Task UpdateSitemapAsync(SitemapUpdateContext context) + foreach (var sitemap in sitemaps) { - var sitemaps = (await _sitemapManager.LoadSitemapsAsync()).Where(s => s.GetType() == typeof(Sitemap)); - - if (!sitemaps.Any()) + // Do not break out of this loop, as it must check each sitemap. + foreach (var source in sitemap.SitemapSources + .Select(s => s as CustomPathSitemapSource)) { - return; - } - - foreach (var sitemap in sitemaps) - { - // Do not break out of this loop, as it must check each sitemap. - foreach (var source in sitemap.SitemapSources - .Select(s => s as CustomPathSitemapSource)) + if (source == null) { - if (source == null) - { - continue; - } - - sitemap.Identifier = IdGenerator.GenerateId(); + continue; } - } - await _sitemapManager.UpdateSitemapAsync(); + sitemap.Identifier = IdGenerator.GenerateId(); + } } + + await _sitemapManager.UpdateSitemapAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs index e5fc591d66f..cf02075136b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/DefaultSitemapUpdateHandler.cs @@ -3,40 +3,39 @@ using System.Threading.Tasks; using OrchardCore.Locking.Distributed; -namespace OrchardCore.Sitemaps.Handlers +namespace OrchardCore.Sitemaps.Handlers; + +public class DefaultSitemapUpdateHandler : ISitemapUpdateHandler { - public class DefaultSitemapUpdateHandler : ISitemapUpdateHandler + private readonly IEnumerable _sitemapTypeUpdateHandlers; + private readonly IDistributedLock _distributedLock; + + public DefaultSitemapUpdateHandler( + IEnumerable sitemapTypeUpdateHandlers, + IDistributedLock distributedLock) + { + _sitemapTypeUpdateHandlers = sitemapTypeUpdateHandlers; + _distributedLock = distributedLock; + } + + public async Task UpdateSitemapAsync(SitemapUpdateContext context) { - private readonly IEnumerable _sitemapTypeUpdateHandlers; - private readonly IDistributedLock _distributedLock; + // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause + // a ConcurrencyException due to the same sitemap document being updated. + + var timeout = TimeSpan.FromMilliseconds(20_000); + (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - public DefaultSitemapUpdateHandler( - IEnumerable sitemapTypeUpdateHandlers, - IDistributedLock distributedLock) + if (!locked) { - _sitemapTypeUpdateHandlers = sitemapTypeUpdateHandlers; - _distributedLock = distributedLock; + throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); } - public async Task UpdateSitemapAsync(SitemapUpdateContext context) + using (locker) { - // Doing the update in a synchronized way makes sure that two simultaneous content item updates don't cause - // a ConcurrencyException due to the same sitemap document being updated. - - var timeout = TimeSpan.FromMilliseconds(20_000); - (var locker, var locked) = await _distributedLock.TryAcquireLockAsync("SITEMAPS_UPDATE_LOCK", timeout, timeout); - - if (!locked) - { - throw new TimeoutException($"Couldn't acquire a lock to update the sitemap within {timeout.Seconds} seconds."); - } - - using (locker) + foreach (var sitemapTypeUpdateHandler in _sitemapTypeUpdateHandlers) { - foreach (var sitemapTypeUpdateHandler in _sitemapTypeUpdateHandlers) - { - await sitemapTypeUpdateHandler.UpdateSitemapAsync(context); - } + await sitemapTypeUpdateHandler.UpdateSitemapAsync(context); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapIndexTypeUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapIndexTypeUpdateHandler.cs index 235febbd27f..0b21a97a262 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapIndexTypeUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapIndexTypeUpdateHandler.cs @@ -5,68 +5,67 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Handlers +namespace OrchardCore.Sitemaps.Handlers; + +public class SitemapIndexTypeUpdateHandler : ISitemapTypeUpdateHandler { - public class SitemapIndexTypeUpdateHandler : ISitemapTypeUpdateHandler - { - private readonly ISitemapManager _sitemapManager; + private readonly ISitemapManager _sitemapManager; - public SitemapIndexTypeUpdateHandler(ISitemapManager sitemapManager) - { - _sitemapManager = sitemapManager; - } + public SitemapIndexTypeUpdateHandler(ISitemapManager sitemapManager) + { + _sitemapManager = sitemapManager; + } - public async Task UpdateSitemapAsync(SitemapUpdateContext context) - { - var contentItem = context.UpdateObject as ContentItem; + public async Task UpdateSitemapAsync(SitemapUpdateContext context) + { + var contentItem = context.UpdateObject as ContentItem; - var allSitemaps = await _sitemapManager.LoadSitemapsAsync(); + var allSitemaps = await _sitemapManager.LoadSitemapsAsync(); - var sitemapIndex = allSitemaps - .FirstOrDefault(s => s.GetType() == typeof(SitemapIndex)); + var sitemapIndex = allSitemaps + .FirstOrDefault(s => s.GetType() == typeof(SitemapIndex)); - if (contentItem == null || sitemapIndex == null) - { - return; - } + if (contentItem == null || sitemapIndex == null) + { + return; + } - var sitemaps = allSitemaps.OfType(); + var sitemaps = allSitemaps.OfType(); - if (!sitemaps.Any()) - { - return; - } + if (!sitemaps.Any()) + { + return; + } - var contentTypeName = contentItem.ContentType; + var contentTypeName = contentItem.ContentType; - foreach (var sitemap in sitemaps) + foreach (var sitemap in sitemaps) + { + foreach (var source in sitemap.SitemapSources.Select(x => x as ContentTypesSitemapSource)) { - foreach (var source in sitemap.SitemapSources.Select(x => x as ContentTypesSitemapSource)) + if (source == null) { - if (source == null) - { - continue; - } + continue; + } - if (source.IndexAll) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } - else if (source.LimitItems && string.Equals(source.LimitedContentType.ContentTypeName, contentTypeName, StringComparison.Ordinal)) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } - else if (source.ContentTypes.Any(ct => string.Equals(ct.ContentTypeName, contentTypeName, StringComparison.Ordinal))) - { - sitemap.Identifier = IdGenerator.GenerateId(); - break; - } + if (source.IndexAll) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; + } + else if (source.LimitItems && string.Equals(source.LimitedContentType.ContentTypeName, contentTypeName, StringComparison.Ordinal)) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; + } + else if (source.ContentTypes.Any(ct => string.Equals(ct.ContentTypeName, contentTypeName, StringComparison.Ordinal))) + { + sitemap.Identifier = IdGenerator.GenerateId(); + break; } } - - await _sitemapManager.UpdateSitemapAsync(); } + + await _sitemapManager.UpdateSitemapAsync(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapPartHandler.cs index b45eb11c1ad..0ebfde23e02 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapPartHandler.cs @@ -3,23 +3,22 @@ using OrchardCore.Sitemaps.Aspects; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Handlers +namespace OrchardCore.Sitemaps.Handlers; + +public class SitemapPartHandler : ContentPartHandler { - public class SitemapPartHandler : ContentPartHandler + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, SitemapPart part) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, SitemapPart part) + return context.ForAsync(aspect => { - return context.ForAsync(aspect => + if (part.OverrideSitemapConfig) { - if (part.OverrideSitemapConfig) - { - aspect.ChangeFrequency = part.ChangeFrequency.ToString(); - aspect.Priority = part.Priority; - aspect.Exclude = part.Exclude; - } + aspect.ChangeFrequency = part.ChangeFrequency.ToString(); + aspect.Priority = part.Priority; + aspect.Exclude = part.Exclude; + } - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapTypeUpdateHandler.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapTypeUpdateHandler.cs index b11ccfd2a25..483f71094af 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapTypeUpdateHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Handlers/SitemapTypeUpdateHandler.cs @@ -1,23 +1,22 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace OrchardCore.Sitemaps.Handlers +namespace OrchardCore.Sitemaps.Handlers; + +public class SitemapTypeUpdateHandler : ISitemapTypeUpdateHandler { - public class SitemapTypeUpdateHandler : ISitemapTypeUpdateHandler - { - private readonly IEnumerable _sitemapSourceUpdateHandlers; + private readonly IEnumerable _sitemapSourceUpdateHandlers; - public SitemapTypeUpdateHandler(IEnumerable sitemapSourceUpdateHandlers) - { - _sitemapSourceUpdateHandlers = sitemapSourceUpdateHandlers; - } + public SitemapTypeUpdateHandler(IEnumerable sitemapSourceUpdateHandlers) + { + _sitemapSourceUpdateHandlers = sitemapSourceUpdateHandlers; + } - public async Task UpdateSitemapAsync(SitemapUpdateContext context) + public async Task UpdateSitemapAsync(SitemapUpdateContext context) + { + foreach (var sitemapSourceUpdateHandler in _sitemapSourceUpdateHandlers) { - foreach (var sitemapSourceUpdateHandler in _sitemapSourceUpdateHandlers) - { - await sitemapSourceUpdateHandler.UpdateSitemapAsync(context); - } + await sitemapSourceUpdateHandler.UpdateSitemapAsync(context); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Migrations.cs index 5d7fdc1f16b..a9497412f70 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Migrations.cs @@ -3,24 +3,23 @@ using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; -namespace OrchardCore.Sitemaps +namespace OrchardCore.Sitemaps; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration - { - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterPartDefinitionAsync("SitemapPart", builder => builder - .Attachable() - .WithDescription("Provides an optional part that allows content items to be excluded, or configured, on a content item.")); + public async Task CreateAsync() + { + await _contentDefinitionManager.AlterPartDefinitionAsync("SitemapPart", builder => builder + .Attachable() + .WithDescription("Provides an optional part that allows content items to be excluded, or configured, on a content item.")); - return 1; - } + return 1; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapDocument.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapDocument.cs index 4b109bc6279..cc48c463be7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapDocument.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Sitemaps.Models +namespace OrchardCore.Sitemaps.Models; + +public class SitemapDocument : Document { - public class SitemapDocument : Document - { - public IDictionary Sitemaps { get; set; } = new Dictionary(); - } + public IDictionary Sitemaps { get; set; } = new Dictionary(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndex.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndex.cs index 1fd578ea7b8..755327e2b72 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndex.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Sitemaps.Models +namespace OrchardCore.Sitemaps.Models; + +public class SitemapIndex : SitemapType { - public class SitemapIndex : SitemapType - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndexSource.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndexSource.cs index 6dd27c11e47..f8f130cb892 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndexSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapIndexSource.cs @@ -1,13 +1,12 @@ -namespace OrchardCore.Sitemaps.Models -{ - // This is a single use Sitemap Index Source. - // It is a SitemapSource because it works better when handling routing lookups. - // but it intentionally is hidden from normal sources and, - // will only ever be used a single time inside a sitemap. - // For that reason there are no drivers. +namespace OrchardCore.Sitemaps.Models; + +// This is a single use Sitemap Index Source. +// It is a SitemapSource because it works better when handling routing lookups. +// but it intentionally is hidden from normal sources and, +// will only ever be used a single time inside a sitemap. +// For that reason there are no drivers. - public class SitemapIndexSource : SitemapSource - { - public string[] ContainedSitemapIds { get; set; } = []; - } +public class SitemapIndexSource : SitemapSource +{ + public string[] ContainedSitemapIds { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapPart.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapPart.cs index 7d238cb5ced..37e1621def0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Models/SitemapPart.cs @@ -1,15 +1,14 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Sitemaps.Models +namespace OrchardCore.Sitemaps.Models; + +/// +/// SitemapPart is an optional part, which allows individual content items to be excluded from sitemaps. +/// +public class SitemapPart : ContentPart { - /// - /// SitemapPart is an optional part, which allows individual content items to be excluded from sitemaps. - /// - public class SitemapPart : ContentPart - { - public bool OverrideSitemapConfig { get; set; } - public ChangeFrequency ChangeFrequency { get; set; } = ChangeFrequency.Daily; - public int Priority { get; set; } = 5; - public bool Exclude { get; set; } - } + public bool OverrideSitemapConfig { get; set; } + public ChangeFrequency ChangeFrequency { get; set; } = ChangeFrequency.Daily; + public int Priority { get; set; } = 5; + public bool Exclude { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Recipes/SitemapsStep.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Recipes/SitemapsStep.cs index 7291387ed65..ebb8a43bb6f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Recipes/SitemapsStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Recipes/SitemapsStep.cs @@ -9,43 +9,42 @@ using OrchardCore.Sitemaps.Models; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Recipes +namespace OrchardCore.Sitemaps.Recipes; + +/// +/// This recipe step creates a set of sitemaps. +/// +public sealed class SitemapsStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of sitemaps. - /// - public sealed class SitemapsStep : IRecipeStepHandler + private readonly ISitemapManager _sitemapManager; + private readonly DocumentJsonSerializerOptions _documentJsonSerializerOptions; + + public SitemapsStep( + ISitemapManager sitemapManager, + IOptions documentJsonSerializerOptions) { - private readonly ISitemapManager _sitemapManager; - private readonly DocumentJsonSerializerOptions _documentJsonSerializerOptions; + _sitemapManager = sitemapManager; + _documentJsonSerializerOptions = documentJsonSerializerOptions.Value; + } - public SitemapsStep( - ISitemapManager sitemapManager, - IOptions documentJsonSerializerOptions) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "Sitemaps", StringComparison.OrdinalIgnoreCase)) { - _sitemapManager = sitemapManager; - _documentJsonSerializerOptions = documentJsonSerializerOptions.Value; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) - { - if (!string.Equals(context.Name, "Sitemaps", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var model = context.Step.ToObject(); + var model = context.Step.ToObject(); - foreach (var token in model.Data.Cast()) - { - var sitemap = token.ToObject(_documentJsonSerializerOptions.SerializerOptions); - await _sitemapManager.UpdateSitemapAsync(sitemap); - } - } - - public sealed class SitemapStepModel + foreach (var token in model.Data.Cast()) { - public JsonArray Data { get; set; } + var sitemap = token.ToObject(_documentJsonSerializerOptions.SerializerOptions); + await _sitemapManager.UpdateSitemapAsync(sitemap); } } + + public sealed class SitemapStepModel + { + public JsonArray Data { get; set; } + } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapEntries.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapEntries.cs index 6e78c73c299..eb03680ac2d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapEntries.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapEntries.cs @@ -1,71 +1,70 @@ using System.Threading.Tasks; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps.Routing +namespace OrchardCore.Sitemaps.Routing; + +public class SitemapEntries { - public class SitemapEntries - { - private readonly ISitemapManager _sitemapManager; + private readonly ISitemapManager _sitemapManager; - private SitemapRouteDocument _document; + private SitemapRouteDocument _document; - public SitemapEntries(ISitemapManager sitemapManager) + public SitemapEntries(ISitemapManager sitemapManager) + { + _sitemapManager = sitemapManager; + } + + public async Task<(bool, string)> TryGetSitemapIdByPathAsync(string path) + { + var identifier = await _sitemapManager.GetIdentifierAsync(); + if (_document == null || _document.Identifier != identifier) { - _sitemapManager = sitemapManager; + await BuildEntriesAsync(identifier); } - public async Task<(bool, string)> TryGetSitemapIdByPathAsync(string path) + if (_document.SitemapIds.TryGetValue(path, out var sitemapId)) { - var identifier = await _sitemapManager.GetIdentifierAsync(); - if (_document == null || _document.Identifier != identifier) - { - await BuildEntriesAsync(identifier); - } + return (true, sitemapId); + } - if (_document.SitemapIds.TryGetValue(path, out var sitemapId)) - { - return (true, sitemapId); - } + return (false, sitemapId); + } - return (false, sitemapId); + public async Task<(bool, string)> TryGetPathBySitemapIdAsync(string sitemapId) + { + var identifier = await _sitemapManager.GetIdentifierAsync(); + if (_document == null || _document.Identifier != identifier) + { + await BuildEntriesAsync(identifier); } - public async Task<(bool, string)> TryGetPathBySitemapIdAsync(string sitemapId) + if (_document.SitemapPaths.TryGetValue(sitemapId, out var path)) { - var identifier = await _sitemapManager.GetIdentifierAsync(); - if (_document == null || _document.Identifier != identifier) - { - await BuildEntriesAsync(identifier); - } - - if (_document.SitemapPaths.TryGetValue(sitemapId, out var path)) - { - return (true, path); - } - - return (false, path); + return (true, path); } - private async Task BuildEntriesAsync(string identifier) + return (false, path); + } + + private async Task BuildEntriesAsync(string identifier) + { + var document = new SitemapRouteDocument() { - var document = new SitemapRouteDocument() - { - Identifier = identifier - }; + Identifier = identifier + }; - var sitemaps = await _sitemapManager.GetSitemapsAsync(); - foreach (var sitemap in sitemaps) + var sitemaps = await _sitemapManager.GetSitemapsAsync(); + foreach (var sitemap in sitemaps) + { + if (!sitemap.Enabled) { - if (!sitemap.Enabled) - { - continue; - } - - document.SitemapIds[sitemap.Path] = sitemap.SitemapId; - document.SitemapPaths[sitemap.SitemapId] = sitemap.Path; + continue; } - _document = document; + document.SitemapIds[sitemap.Path] = sitemap.SitemapId; + document.SitemapPaths[sitemap.SitemapId] = sitemap.Path; } + + _document = document; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteDocument.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteDocument.cs index 2a14bb10e4c..93a9a81761d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteDocument.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Sitemaps.Routing +namespace OrchardCore.Sitemaps.Routing; + +public class SitemapRouteDocument : Document { - public class SitemapRouteDocument : Document - { - public Dictionary SitemapIds { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - public Dictionary SitemapPaths { get; set; } = []; - } + public Dictionary SitemapIds { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + public Dictionary SitemapPaths { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteTransformer.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteTransformer.cs index 9c695f0b094..602d3a44e25 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteTransformer.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapRouteTransformer.cs @@ -4,40 +4,39 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Options; -namespace OrchardCore.Sitemaps.Routing +namespace OrchardCore.Sitemaps.Routing; + +public class SitemapRouteTransformer : DynamicRouteValueTransformer { - public class SitemapRouteTransformer : DynamicRouteValueTransformer + private readonly SitemapEntries _entries; + private readonly SitemapsOptions _options; + + public SitemapRouteTransformer(SitemapEntries entries, IOptions options) { - private readonly SitemapEntries _entries; - private readonly SitemapsOptions _options; + _entries = entries; + _options = options.Value; + } - public SitemapRouteTransformer(SitemapEntries entries, IOptions options) - { - _entries = entries; - _options = options.Value; - } + public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + // Use route value provided by SitemapTransformer template. + var path = values["sitemap"] as string; - public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + if (!string.IsNullOrEmpty(path)) { - // Use route value provided by SitemapTransformer template. - var path = values["sitemap"] as string; + (var found, var sitemapId) = await _entries.TryGetSitemapIdByPathAsync(path); - if (!string.IsNullOrEmpty(path)) + if (found) { - (var found, var sitemapId) = await _entries.TryGetSitemapIdByPathAsync(path); - - if (found) + var routeValues = new RouteValueDictionary(_options.GlobalRouteValues) { - var routeValues = new RouteValueDictionary(_options.GlobalRouteValues) - { - [_options.SitemapIdKey] = sitemapId - }; + [_options.SitemapIdKey] = sitemapId + }; - return routeValues; - } + return routeValues; } - - return null; } + + return null; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapValuesAddressScheme.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapValuesAddressScheme.cs index 6b62155db10..e1adccef57b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapValuesAddressScheme.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Routing/SitemapValuesAddressScheme.cs @@ -6,86 +6,85 @@ using Microsoft.Extensions.Options; using OrchardCore.Routing; -namespace OrchardCore.Sitemaps.Routing +namespace OrchardCore.Sitemaps.Routing; + +internal sealed class SitemapValuesAddressScheme : IShellRouteValuesAddressScheme { - internal sealed class SitemapValuesAddressScheme : IShellRouteValuesAddressScheme + private readonly SitemapEntries _entries; + private readonly SitemapsOptions _options; + + public SitemapValuesAddressScheme(SitemapEntries entries, IOptions options) { - private readonly SitemapEntries _entries; - private readonly SitemapsOptions _options; + _entries = entries; + _options = options.Value; + } - public SitemapValuesAddressScheme(SitemapEntries entries, IOptions options) + public IEnumerable FindEndpoints(RouteValuesAddress address) + { + if (address.AmbientValues == null || address.ExplicitValues == null) { - _entries = entries; - _options = options.Value; + return []; } - public IEnumerable FindEndpoints(RouteValuesAddress address) - { - if (address.AmbientValues == null || address.ExplicitValues == null) - { - return []; - } + var sitemapId = address.ExplicitValues[_options.SitemapIdKey]?.ToString(); - var sitemapId = address.ExplicitValues[_options.SitemapIdKey]?.ToString(); + if (string.IsNullOrEmpty(sitemapId)) + { + return []; + } - if (string.IsNullOrEmpty(sitemapId)) - { - return []; - } + (var found, var path) = _entries.TryGetPathBySitemapIdAsync(sitemapId).GetAwaiter().GetResult(); - (var found, var path) = _entries.TryGetPathBySitemapIdAsync(sitemapId).GetAwaiter().GetResult(); + if (!found) + { + return []; + } - if (!found) - { - return []; - } + if (Match(address.ExplicitValues)) + { + var routeValues = new RouteValueDictionary(address.ExplicitValues); - if (Match(address.ExplicitValues)) + if (address.ExplicitValues.Count > _options.GlobalRouteValues.Count + 1) { - var routeValues = new RouteValueDictionary(address.ExplicitValues); - - if (address.ExplicitValues.Count > _options.GlobalRouteValues.Count + 1) + foreach (var entry in address.ExplicitValues) { - foreach (var entry in address.ExplicitValues) + if (string.Equals(entry.Key, _options.SitemapIdKey, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(entry.Key, _options.SitemapIdKey, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (!_options.GlobalRouteValues.ContainsKey(entry.Key)) - { - routeValues.Remove(entry.Key); - } + continue; } - } - - var endpoint = new RouteEndpoint - ( - c => null, - RoutePatternFactory.Parse(path, routeValues, null), - 0, - null, - null - ); - return [endpoint]; + if (!_options.GlobalRouteValues.ContainsKey(entry.Key)) + { + routeValues.Remove(entry.Key); + } + } } - return []; + var endpoint = new RouteEndpoint + ( + c => null, + RoutePatternFactory.Parse(path, routeValues, null), + 0, + null, + null + ); + + return [endpoint]; } - private bool Match(RouteValueDictionary explicitValues) + return []; + } + + private bool Match(RouteValueDictionary explicitValues) + { + foreach (var entry in _options.GlobalRouteValues) { - foreach (var entry in _options.GlobalRouteValues) + if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) { - if (!string.Equals(explicitValues[entry.Key]?.ToString(), entry.Value?.ToString(), StringComparison.OrdinalIgnoreCase)) - { - return false; - } + return false; } - - return true; } + + return true; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/DefaultRouteableContentTypeCoordinator.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/DefaultRouteableContentTypeCoordinator.cs index 74b0c557f1f..fbe84c740ff 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/DefaultRouteableContentTypeCoordinator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/DefaultRouteableContentTypeCoordinator.cs @@ -4,41 +4,40 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Sitemaps.Builders; -namespace OrchardCore.Sitemaps.Services +namespace OrchardCore.Sitemaps.Services; + +public class DefaultRouteableContentTypeCoordinator : IRouteableContentTypeCoordinator { - public class DefaultRouteableContentTypeCoordinator : IRouteableContentTypeCoordinator - { - private readonly IEnumerable _routeableContentTypeProviders; + private readonly IEnumerable _routeableContentTypeProviders; - public DefaultRouteableContentTypeCoordinator(IEnumerable routeableContentTypeProviders) - { - _routeableContentTypeProviders = routeableContentTypeProviders; - } + public DefaultRouteableContentTypeCoordinator(IEnumerable routeableContentTypeProviders) + { + _routeableContentTypeProviders = routeableContentTypeProviders; + } - public async Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) + public async Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) + { + foreach (var rctp in _routeableContentTypeProviders) { - foreach (var rctp in _routeableContentTypeProviders) + var result = await rctp.GetRouteAsync(context, contentItem); + if (result != null) { - var result = await rctp.GetRouteAsync(context, contentItem); - if (result != null) - { - return result; - } + return result; } - - return null; } - public async Task> ListRoutableTypeDefinitionsAsync() - { - var results = new List(); + return null; + } - foreach (var routeableContentTypeProvider in _routeableContentTypeProviders) - { - results.AddRange(await routeableContentTypeProvider.ListRoutableTypeDefinitionsAsync()); - } + public async Task> ListRoutableTypeDefinitionsAsync() + { + var results = new List(); - return results; + foreach (var routeableContentTypeProvider in _routeableContentTypeProviders) + { + results.AddRange(await routeableContentTypeProvider.ListRoutableTypeDefinitionsAsync()); } + + return results; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/RazorPagesContentTypeProvider.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/RazorPagesContentTypeProvider.cs index 5c6391758d0..dffaf743f77 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/RazorPagesContentTypeProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/RazorPagesContentTypeProvider.cs @@ -8,47 +8,46 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Sitemaps.Builders; -namespace OrchardCore.Sitemaps.Services +namespace OrchardCore.Sitemaps.Services; + +public class RazorPagesContentTypeProvider : IRouteableContentTypeProvider { - public class RazorPagesContentTypeProvider : IRouteableContentTypeProvider + private readonly SitemapsRazorPagesOptions _options; + private readonly IContentDefinitionManager _contentDefinitionManager; + + public RazorPagesContentTypeProvider( + IOptions options, + IContentDefinitionManager contentDefinitionManager + ) { - private readonly SitemapsRazorPagesOptions _options; - private readonly IContentDefinitionManager _contentDefinitionManager; + _options = options.Value; + _contentDefinitionManager = contentDefinitionManager; + } - public RazorPagesContentTypeProvider( - IOptions options, - IContentDefinitionManager contentDefinitionManager - ) + public Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) + { + var option = _options.ContentTypeOptions.FirstOrDefault(o => o.ContentType == contentItem.ContentType); + if (option != null && option.RouteValues != null) { - _options = options.Value; - _contentDefinitionManager = contentDefinitionManager; - } + var pageName = string.IsNullOrEmpty(option.PageName) ? option.ContentType : option.PageName; - public Task GetRouteAsync(SitemapBuilderContext context, ContentItem contentItem) - { - var option = _options.ContentTypeOptions.FirstOrDefault(o => o.ContentType == contentItem.ContentType); - if (option != null && option.RouteValues != null) + // When used from outside a razor page name must start with a / + if (!pageName.StartsWith('/')) { - var pageName = string.IsNullOrEmpty(option.PageName) ? option.ContentType : option.PageName; - - // When used from outside a razor page name must start with a / - if (!pageName.StartsWith('/')) - { - pageName = '/' + pageName; - } - - var url = context.HostPrefix + context.UrlHelper.Page(pageName, option.RouteValues.Invoke(contentItem)); - return Task.FromResult(url); + pageName = '/' + pageName; } - return Task.FromResult(null); + var url = context.HostPrefix + context.UrlHelper.Page(pageName, option.RouteValues.Invoke(contentItem)); + return Task.FromResult(url); } - public async Task> ListRoutableTypeDefinitionsAsync() - { - var definitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + return Task.FromResult(null); + } - return definitions.Where(definition => _options.ContentTypeOptions.Any(o => o.ContentType == definition.Name)); - } + public async Task> ListRoutableTypeDefinitionsAsync() + { + var definitions = await _contentDefinitionManager.ListTypeDefinitionsAsync(); + + return definitions.Where(definition => _options.ContentTypeOptions.Any(o => o.ContentType == definition.Name)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapHelperService.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapHelperService.cs index 8aaa0df2b1f..252be621ca7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapHelperService.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapHelperService.cs @@ -7,78 +7,77 @@ using OrchardCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Services +namespace OrchardCore.Sitemaps.Services; + +public class SitemapHelperService : ISitemapHelperService { - public class SitemapHelperService : ISitemapHelperService - { - // Path requirements for sitemaps include . as acceptable character. - public static readonly char[] InvalidCharactersForPath = ":?#[]@!$&'()*+,;=<>\\|%".ToCharArray(); - public const int MaxPathLength = 1024; - public const string Prefix = ""; - public const string Path = "Path"; + // Path requirements for sitemaps include . as acceptable character. + public static readonly char[] InvalidCharactersForPath = ":?#[]@!$&'()*+,;=<>\\|%".ToCharArray(); + public const int MaxPathLength = 1024; + public const string Prefix = ""; + public const string Path = "Path"; + + private readonly ISlugService _slugService; + private readonly ISitemapManager _sitemapManager; + protected readonly IStringLocalizer S; - private readonly ISlugService _slugService; - private readonly ISitemapManager _sitemapManager; - protected readonly IStringLocalizer S; + public SitemapHelperService( + ISlugService slugService, + ISitemapManager sitemapManager, + IStringLocalizer stringLocalizer + ) + { + _slugService = slugService; + _sitemapManager = sitemapManager; + S = stringLocalizer; + } - public SitemapHelperService( - ISlugService slugService, - ISitemapManager sitemapManager, - IStringLocalizer stringLocalizer - ) + public async Task ValidatePathAsync(string path, IUpdateModel updater, string sitemapId = null) + { + // Keep localized text as similar to Autoroute as possible. + if (path == "/") { - _slugService = slugService; - _sitemapManager = sitemapManager; - S = stringLocalizer; + updater.ModelState.AddModelError(Prefix, Path, S["Your permalink can't be set to the homepage"]); } - public async Task ValidatePathAsync(string path, IUpdateModel updater, string sitemapId = null) + if (path.IndexOfAny(InvalidCharactersForPath) > -1 || path.IndexOf(' ') > -1) { - // Keep localized text as similar to Autoroute as possible. - if (path == "/") - { - updater.ModelState.AddModelError(Prefix, Path, S["Your permalink can't be set to the homepage"]); - } - - if (path.IndexOfAny(InvalidCharactersForPath) > -1 || path.IndexOf(' ') > -1) - { - var invalidCharactersForMessage = string.Join(", ", InvalidCharactersForPath.Select(c => $"\"{c}\"")); - updater.ModelState.AddModelError(Prefix, Path, S["Please do not use any of the following characters in your permalink: {0}. No spaces are allowed (please use dashes or underscores instead).", invalidCharactersForMessage]); - } - - // Precludes possibility of collision with Autoroute as Autoroute excludes . as a valid path character. - if (!path.EndsWith(Sitemap.PathExtension)) - { - updater.ModelState.AddModelError(Prefix, Path, S["Your permalink must end with {0}.", Sitemap.PathExtension]); - } + var invalidCharactersForMessage = string.Join(", ", InvalidCharactersForPath.Select(c => $"\"{c}\"")); + updater.ModelState.AddModelError(Prefix, Path, S["Please do not use any of the following characters in your permalink: {0}. No spaces are allowed (please use dashes or underscores instead).", invalidCharactersForMessage]); + } - if (path.Length > MaxPathLength) - { - updater.ModelState.AddModelError(Prefix, Path, S["Your permalink is too long. The permalink can only be up to {0} characters.", MaxPathLength]); - } + // Precludes possibility of collision with Autoroute as Autoroute excludes . as a valid path character. + if (!path.EndsWith(Sitemap.PathExtension)) + { + updater.ModelState.AddModelError(Prefix, Path, S["Your permalink must end with {0}.", Sitemap.PathExtension]); + } - var routeExists = false; - if (string.IsNullOrEmpty(sitemapId)) - { - routeExists = (await _sitemapManager.GetSitemapsAsync()) - .Any(p => string.Equals(p.Path, path.TrimStart('/'), StringComparison.OrdinalIgnoreCase)); - } - else - { - routeExists = (await _sitemapManager.GetSitemapsAsync()) - .Any(p => p.SitemapId != sitemapId && string.Equals(p.Path, path.TrimStart('/'), StringComparison.OrdinalIgnoreCase)); - } + if (path.Length > MaxPathLength) + { + updater.ModelState.AddModelError(Prefix, Path, S["Your permalink is too long. The permalink can only be up to {0} characters.", MaxPathLength]); + } - if (routeExists) - { - updater.ModelState.AddModelError(Prefix, Path, S["Your permalink is already in use."]); - } + var routeExists = false; + if (string.IsNullOrEmpty(sitemapId)) + { + routeExists = (await _sitemapManager.GetSitemapsAsync()) + .Any(p => string.Equals(p.Path, path.TrimStart('/'), StringComparison.OrdinalIgnoreCase)); + } + else + { + routeExists = (await _sitemapManager.GetSitemapsAsync()) + .Any(p => p.SitemapId != sitemapId && string.Equals(p.Path, path.TrimStart('/'), StringComparison.OrdinalIgnoreCase)); } - public string GetSitemapSlug(string name) + if (routeExists) { - return _slugService.Slugify(name) + Sitemap.PathExtension; + updater.ModelState.AddModelError(Prefix, Path, S["Your permalink is already in use."]); } + } + public string GetSitemapSlug(string name) + { + return _slugService.Slugify(name) + Sitemap.PathExtension; } + } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapIdGenerator.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapIdGenerator.cs index e30a4bd9840..a184f6ca913 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapIdGenerator.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapIdGenerator.cs @@ -1,19 +1,18 @@ using OrchardCore.Entities; -namespace OrchardCore.Sitemaps.Services +namespace OrchardCore.Sitemaps.Services; + +public class SitemapIdGenerator : ISitemapIdGenerator { - public class SitemapIdGenerator : ISitemapIdGenerator - { - private readonly IIdGenerator _idGenerator; + private readonly IIdGenerator _idGenerator; - public SitemapIdGenerator(IIdGenerator idGenerator) - { - _idGenerator = idGenerator; - } + public SitemapIdGenerator(IIdGenerator idGenerator) + { + _idGenerator = idGenerator; + } - public string GenerateUniqueId() - { - return _idGenerator.GenerateUniqueId(); - } + public string GenerateUniqueId() + { + return _idGenerator.GenerateUniqueId(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapManager.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapManager.cs index c74cae93d63..f9abb79333f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapManager.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Services/SitemapManager.cs @@ -4,80 +4,79 @@ using OrchardCore.Documents; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.Services +namespace OrchardCore.Sitemaps.Services; + +public class SitemapManager : ISitemapManager { - public class SitemapManager : ISitemapManager - { - private readonly IDocumentManager _documentManager; + private readonly IDocumentManager _documentManager; - public SitemapManager(IDocumentManager documentManager) - { - _documentManager = documentManager; - } + public SitemapManager(IDocumentManager documentManager) + { + _documentManager = documentManager; + } - public async Task GetIdentifierAsync() => (await GetDocumentAsync()).Identifier; + public async Task GetIdentifierAsync() => (await GetDocumentAsync()).Identifier; - public async Task> LoadSitemapsAsync() - { - return (await LoadDocumentAsync()).Sitemaps.Values.ToArray(); - } + public async Task> LoadSitemapsAsync() + { + return (await LoadDocumentAsync()).Sitemaps.Values.ToArray(); + } - public async Task> GetSitemapsAsync() - { - return (await GetDocumentAsync()).Sitemaps.Values.ToArray(); - } + public async Task> GetSitemapsAsync() + { + return (await GetDocumentAsync()).Sitemaps.Values.ToArray(); + } - public async Task LoadSitemapAsync(string sitemapId) + public async Task LoadSitemapAsync(string sitemapId) + { + var document = await LoadDocumentAsync(); + if (document.Sitemaps.TryGetValue(sitemapId, out var sitemap)) { - var document = await LoadDocumentAsync(); - if (document.Sitemaps.TryGetValue(sitemapId, out var sitemap)) - { - return sitemap; - } - - return null; + return sitemap; } - public async Task GetSitemapAsync(string sitemapId) - { - var document = await GetDocumentAsync(); - if (document.Sitemaps.TryGetValue(sitemapId, out var sitemap)) - { - return sitemap; - } - - return null; - } + return null; + } - public async Task DeleteSitemapAsync(string sitemapId) + public async Task GetSitemapAsync(string sitemapId) + { + var document = await GetDocumentAsync(); + if (document.Sitemaps.TryGetValue(sitemapId, out var sitemap)) { - var existing = await LoadDocumentAsync(); - existing.Sitemaps.Remove(sitemapId); - await _documentManager.UpdateAsync(existing); + return sitemap; } - public async Task UpdateSitemapAsync(SitemapType sitemap) - { - var existing = await LoadDocumentAsync(); - existing.Sitemaps[sitemap.SitemapId] = sitemap; - sitemap.Identifier = IdGenerator.GenerateId(); - await _documentManager.UpdateAsync(existing); - } + return null; + } - public async Task UpdateSitemapAsync() - { - var existing = await LoadDocumentAsync(); - await _documentManager.UpdateAsync(existing); - } + public async Task DeleteSitemapAsync(string sitemapId) + { + var existing = await LoadDocumentAsync(); + existing.Sitemaps.Remove(sitemapId); + await _documentManager.UpdateAsync(existing); + } - /// - /// Loads the sitemap document from the store for updating and that should not be cached. - /// - private Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); + public async Task UpdateSitemapAsync(SitemapType sitemap) + { + var existing = await LoadDocumentAsync(); + existing.Sitemaps[sitemap.SitemapId] = sitemap; + sitemap.Identifier = IdGenerator.GenerateId(); + await _documentManager.UpdateAsync(existing); + } - /// - /// Gets the sitemap document from the cache for sharing and that should not be updated. - /// - private Task GetDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); + public async Task UpdateSitemapAsync() + { + var existing = await LoadDocumentAsync(); + await _documentManager.UpdateAsync(existing); } + + /// + /// Loads the sitemap document from the store for updating and that should not be cached. + /// + private Task LoadDocumentAsync() => _documentManager.GetOrCreateMutableAsync(); + + /// + /// Gets the sitemap document from the cache for sharing and that should not be updated. + /// + private Task GetDocumentAsync() => _documentManager.GetOrCreateImmutableAsync(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Startup.cs index 3a403bc06a9..488fdbbfb4b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/Startup.cs @@ -26,113 +26,112 @@ using OrchardCore.Sitemaps.Routing; using OrchardCore.Sitemaps.Services; -namespace OrchardCore.Sitemaps +namespace OrchardCore.Sitemaps; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDataMigration(); - services.AddScoped(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); + services.AddScoped(); - services.Configure(options => + services.Configure(options => + { + if (options.GlobalRouteValues.Count == 0) { - if (options.GlobalRouteValues.Count == 0) + options.GlobalRouteValues = new RouteValueDictionary { - options.GlobalRouteValues = new RouteValueDictionary - { - {"Area", "OrchardCore.Sitemaps"}, - {"Controller", "Sitemap"}, - {"Action", "Index"} - }; - - options.SitemapIdKey = "sitemapId"; - } - }); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Sitemap Part. - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); - - // Custom sitemap path. - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, CustomPathSitemapSourceDriver>(); - services.AddScoped>(); - - services.AddRecipeExecutionStep(); - - // Allows to serialize 'SitemapType' derived types. - services.AddJsonDerivedTypeInfo(); - services.AddJsonDerivedTypeInfo(); - - // Allows to serialize 'SitemapSource' derived types. - services.AddJsonDerivedTypeInfo(); - services.AddJsonDerivedTypeInfo(); - services.AddJsonDerivedTypeInfo(); - } - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - { - routes.MapDynamicControllerRoute("/{**sitemap}"); - } + {"Area", "OrchardCore.Sitemaps"}, + {"Controller", "Sitemap"}, + {"Action", "Index"} + }; + + options.SitemapIdKey = "sitemapId"; + } + }); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + // Sitemap Part. + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); + + // Custom sitemap path. + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, CustomPathSitemapSourceDriver>(); + services.AddScoped>(); + + services.AddRecipeExecutionStep(); + + // Allows to serialize 'SitemapType' derived types. + services.AddJsonDerivedTypeInfo(); + services.AddJsonDerivedTypeInfo(); + + // Allows to serialize 'SitemapSource' derived types. + services.AddJsonDerivedTypeInfo(); + services.AddJsonDerivedTypeInfo(); + services.AddJsonDerivedTypeInfo(); } - [Feature("OrchardCore.Sitemaps.RazorPages")] - public sealed class SitemapsRazorPagesStartup : StartupBase + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddOptions(); - services.AddScoped(); - } + routes.MapDynamicControllerRoute("/{**sitemap}"); } +} - [Feature("OrchardCore.Sitemaps.Cleanup")] - public sealed class SitemapsCleanupStartup : StartupBase +[Feature("OrchardCore.Sitemaps.RazorPages")] +public sealed class SitemapsRazorPagesStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - } + services.AddOptions(); + services.AddScoped(); } +} - [RequireFeatures("OrchardCore.Deployment", "OrchardCore.Sitemaps")] - public sealed class SitemapsDeploymentStartup : StartupBase +[Feature("OrchardCore.Sitemaps.Cleanup")] +public sealed class SitemapsCleanupStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddDeployment(); - } + services.AddSingleton(); } +} - [RequireFeatures("OrchardCore.Seo")] - public sealed class SeoStartup : StartupBase +[RequireFeatures("OrchardCore.Deployment", "OrchardCore.Sitemaps")] +public sealed class SitemapsDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient(); - services.AddScoped, SitemapsRobotsSettingsDisplayDriver>(); - } + services.AddDeployment(); + } +} + +[RequireFeatures("OrchardCore.Seo")] +public sealed class SeoStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + services.AddScoped, SitemapsRobotsSettingsDisplayDriver>(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapIndexViewModel.cs index a2b3bfac5f2..891c510a2b5 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapIndexViewModel.cs @@ -1,25 +1,24 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class CreateSitemapIndexViewModel { - public class CreateSitemapIndexViewModel - { - [Required] - public string Name { get; set; } + [Required] + public string Name { get; set; } - [Required] - public string Path { get; set; } + [Required] + public string Path { get; set; } - public bool Enabled { get; set; } + public bool Enabled { get; set; } - public ContainableSitemapEntryViewModel[] ContainableSitemaps { get; set; } = []; + public ContainableSitemapEntryViewModel[] ContainableSitemaps { get; set; } = []; - } +} - public class ContainableSitemapEntryViewModel - { - public bool IsChecked { get; set; } - public string SitemapId { get; set; } - public string Name { get; set; } - } +public class ContainableSitemapEntryViewModel +{ + public bool IsChecked { get; set; } + public string SitemapId { get; set; } + public string Name { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapViewModel.cs index 6553b72c947..db6251554cf 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSitemapViewModel.cs @@ -1,14 +1,13 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class CreateSitemapViewModel { - public class CreateSitemapViewModel - { - [Required] - public string Name { get; set; } + [Required] + public string Name { get; set; } - public string Path { get; set; } + public string Path { get; set; } - public bool Enabled { get; set; } = true; - } + public bool Enabled { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSourceViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSourceViewModel.cs index 12bf01a2b96..7cbb2a3575e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSourceViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CreateSourceViewModel.cs @@ -1,17 +1,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class CreateSourceViewModel { - public class CreateSourceViewModel - { - public string SitemapId { get; set; } - public string SitemapSourceId { get; set; } - public string SitemapSourceType { get; set; } - public dynamic Editor { get; set; } + public string SitemapId { get; set; } + public string SitemapSourceId { get; set; } + public string SitemapSourceType { get; set; } + public dynamic Editor { get; set; } - [BindNever] - public SitemapSource SitemapSource { get; set; } + [BindNever] + public SitemapSource SitemapSource { get; set; } - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CustomPathSitemapSourceViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CustomPathSitemapSourceViewModel.cs index 65664699d6a..9e0d5840be7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CustomPathSitemapSourceViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/CustomPathSitemapSourceViewModel.cs @@ -1,25 +1,24 @@ using System.ComponentModel.DataAnnotations; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +/// +/// A sitemap source for managing custom url. +/// +public class CustomPathSitemapSourceViewModel { /// - /// A sitemap source for managing custom url. + /// Gets and sets the custom url. /// - public class CustomPathSitemapSourceViewModel - { - /// - /// Gets and sets the custom url. - /// - [Required] - public string Path { get; set; } + [Required] + public string Path { get; set; } - /// - /// Gets and sets the frequency to apply to sitemap entries. - /// - public ChangeFrequency ChangeFrequency { get; set; } + /// + /// Gets and sets the frequency to apply to sitemap entries. + /// + public ChangeFrequency ChangeFrequency { get; set; } - // Handle as int, and convert to float, when building, to support localization. - public int Priority { get; set; } = 5; - } + // Handle as int, and convert to float, when building, to support localization. + public int Priority { get; set; } = 5; } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/DisplaySitemapViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/DisplaySitemapViewModel.cs index 265a7aa5711..515fe50e869 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/DisplaySitemapViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/DisplaySitemapViewModel.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class DisplaySitemapViewModel { - public class DisplaySitemapViewModel - { - public SitemapType Sitemap { get; set; } - public IEnumerable Items { get; set; } - public IDictionary Thumbnails { get; set; } - } + public SitemapType Sitemap { get; set; } + public IEnumerable Items { get; set; } + public IDictionary Thumbnails { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapIndexViewModel.cs index 3966676813a..190b166bd00 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapIndexViewModel.cs @@ -2,14 +2,13 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class EditSitemapIndexViewModel : CreateSitemapIndexViewModel { - public class EditSitemapIndexViewModel : CreateSitemapIndexViewModel - { - [Required] - public string SitemapId { get; set; } + [Required] + public string SitemapId { get; set; } - [BindNever] - public SitemapIndexSource SitemapIndexSource { get; set; } - } + [BindNever] + public SitemapIndexSource SitemapIndexSource { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapViewModel.cs index 3346b664198..b0d3825767b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSitemapViewModel.cs @@ -1,16 +1,15 @@ using System.ComponentModel.DataAnnotations; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class EditSitemapViewModel { - public class EditSitemapViewModel - { - public string SitemapId { get; set; } + public string SitemapId { get; set; } - [Required] - public string Name { get; set; } + [Required] + public string Name { get; set; } - public string Path { get; set; } + public string Path { get; set; } - public bool Enabled { get; set; } - } + public bool Enabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSourceViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSourceViewModel.cs index 494c288db5a..bbffaa06029 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSourceViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/EditSourceViewModel.cs @@ -1,16 +1,15 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class EditSourceViewModel { - public class EditSourceViewModel - { - public string SitemapId { get; set; } - public string SitemapSourceId { get; set; } - public dynamic Editor { get; set; } + public string SitemapId { get; set; } + public string SitemapSourceId { get; set; } + public dynamic Editor { get; set; } - [BindNever] - public SitemapSource SitemapSource { get; set; } + [BindNever] + public SitemapSource SitemapSource { get; set; } - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapCacheViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapCacheViewModel.cs index 0b265170313..99553a79fe8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapCacheViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapCacheViewModel.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class ListSitemapCacheViewModel { - public class ListSitemapCacheViewModel - { - public string[] CachedFileNames { get; set; } - } + public string[] CachedFileNames { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapIndexViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapIndexViewModel.cs index bb2fd1fad9b..a87010bab0c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapIndexViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapIndexViewModel.cs @@ -1,18 +1,17 @@ using System.Collections.Generic; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class ListSitemapIndexViewModel { - public class ListSitemapIndexViewModel - { - public IList SitemapIndexes { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - public dynamic Pager { get; set; } - } + public IList SitemapIndexes { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + public dynamic Pager { get; set; } +} - public class SitemapIndexListEntry - { - public string SitemapId { get; set; } - public string Name { get; set; } - public bool Enabled { get; set; } - } +public class SitemapIndexListEntry +{ + public string SitemapId { get; set; } + public string Name { get; set; } + public bool Enabled { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapViewModel.cs index 5174fa4e893..ca996311894 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/ListSitemapViewModel.cs @@ -2,39 +2,37 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class ListSitemapViewModel +{ + public IList Sitemaps { get; set; } + public ContentOptions Options { get; set; } = new ContentOptions(); + public dynamic Pager { get; set; } +} + +public class SitemapListEntry +{ + public string SitemapId { get; set; } + public string Name { get; set; } + public bool Enabled { get; set; } +} + +public class ContentOptions { - public class ListSitemapViewModel - { - public IList Sitemaps { get; set; } - public ContentOptions Options { get; set; } = new ContentOptions(); - public dynamic Pager { get; set; } - } - - public class SitemapListEntry - { - public string SitemapId { get; set; } - public string Name { get; set; } - public bool Enabled { get; set; } - } - - public class ContentOptions - { - public string Search { get; set; } - public ContentsBulkAction BulkAction { get; set; } - - #region Lists to populate - - [BindNever] - public List ContentsBulkAction { get; set; } - - #endregion Lists to populate - } - - public enum ContentsBulkAction - { - None, - Remove - } + public string Search { get; set; } + public ContentsBulkAction BulkAction { get; set; } + + #region Lists to populate + + [BindNever] + public List ContentsBulkAction { get; set; } + #endregion Lists to populate +} + +public enum ContentsBulkAction +{ + None, + Remove } diff --git a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/SitemapPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/SitemapPartViewModel.cs index 03f7a700e69..7d7637cc9c4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/SitemapPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Sitemaps/ViewModels/SitemapPartViewModel.cs @@ -1,19 +1,18 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Sitemaps.Models; -namespace OrchardCore.Sitemaps.ViewModels +namespace OrchardCore.Sitemaps.ViewModels; + +public class SitemapPartViewModel { - public class SitemapPartViewModel - { - public bool OverrideSitemapConfig { get; set; } + public bool OverrideSitemapConfig { get; set; } - public ChangeFrequency ChangeFrequency { get; set; } + public ChangeFrequency ChangeFrequency { get; set; } - public int Priority { get; set; } + public int Priority { get; set; } - public bool Exclude { get; set; } + public bool Exclude { get; set; } - [BindNever] - public SitemapPart SitemapPart { get; set; } - } + [BindNever] + public SitemapPart SitemapPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldDisplayDriver.cs index 36d3cf69726..968e2139617 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldDisplayDriver.cs @@ -13,95 +13,94 @@ using OrchardCore.Spatial.Settings; using OrchardCore.Spatial.ViewModels; -namespace OrchardCore.Spatial.Drivers +namespace OrchardCore.Spatial.Drivers; + +public sealed class GeoPointFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class GeoPointFieldDisplayDriver : ContentFieldDisplayDriver + internal readonly IStringLocalizer S; + + public GeoPointFieldDisplayDriver(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public GeoPointFieldDisplayDriver(IStringLocalizer localizer) + public override IDisplayResult Display(GeoPointField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - S = localizer; - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }).Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Display(GeoPointField field, BuildFieldDisplayContext context) + public override IDisplayResult Edit(GeoPointField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }).Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Latitude = Convert.ToString(field.Latitude, CultureInfo.InvariantCulture); + model.Longitude = Convert.ToString(field.Longitude, CultureInfo.InvariantCulture); + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - public override IDisplayResult Edit(GeoPointField field, BuildFieldEditorContext context) - { - return Initialize(GetEditorShapeType(context), model => - { - model.Latitude = Convert.ToString(field.Latitude, CultureInfo.InvariantCulture); - model.Longitude = Convert.ToString(field.Longitude, CultureInfo.InvariantCulture); - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + public override async Task UpdateAsync(GeoPointField field, UpdateFieldEditorContext context) + { + var viewModel = new EditGeoPointFieldViewModel(); + + var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Latitude, f => f.Longitude); - public override async Task UpdateAsync(GeoPointField field, UpdateFieldEditorContext context) + if (modelUpdated) { - var viewModel = new EditGeoPointFieldViewModel(); + decimal latitude; + decimal longitude; - var modelUpdated = await context.Updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Latitude, f => f.Longitude); + var settings = context.PartFieldDefinition.GetSettings(); - if (modelUpdated) + if (string.IsNullOrWhiteSpace(viewModel.Latitude)) { - decimal latitude; - decimal longitude; - - var settings = context.PartFieldDefinition.GetSettings(); - - if (string.IsNullOrWhiteSpace(viewModel.Latitude)) + if (settings.Required) { - if (settings.Required) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Latitude), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); - } - else - { - field.Latitude = null; - } - } - else if (!decimal.TryParse(viewModel.Latitude, NumberStyles.Any, CultureInfo.InvariantCulture, out latitude)) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Latitude), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Latitude), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); } else { - field.Latitude = latitude; + field.Latitude = null; } + } + else if (!decimal.TryParse(viewModel.Latitude, NumberStyles.Any, CultureInfo.InvariantCulture, out latitude)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Latitude), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + } + else + { + field.Latitude = latitude; + } - if (string.IsNullOrWhiteSpace(viewModel.Longitude)) - { - if (settings.Required) - { - context.Updater.ModelState.AddModelError(Prefix, nameof(field.Longitude), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); - } - else - { - field.Longitude = null; - } - } - else if (!decimal.TryParse(viewModel.Longitude, NumberStyles.Any, CultureInfo.InvariantCulture, out longitude)) + if (string.IsNullOrWhiteSpace(viewModel.Longitude)) + { + if (settings.Required) { - context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Longitude), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + context.Updater.ModelState.AddModelError(Prefix, nameof(field.Longitude), S["The {0} field is required.", context.PartFieldDefinition.DisplayName()]); } else { - field.Longitude = longitude; + field.Longitude = null; } } - - return Edit(field, context); + else if (!decimal.TryParse(viewModel.Longitude, NumberStyles.Any, CultureInfo.InvariantCulture, out longitude)) + { + context.Updater.ModelState.AddModelError(Prefix, nameof(viewModel.Longitude), S["{0} is an invalid number.", context.PartFieldDefinition.DisplayName()]); + } + else + { + field.Longitude = longitude; + } } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldSettingsDriver.cs index f159c482549..7fb8f939a76 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Drivers/GeoPointFieldSettingsDriver.cs @@ -7,30 +7,29 @@ using OrchardCore.Spatial.Fields; using OrchardCore.Spatial.Settings; -namespace OrchardCore.Spatial.Drivers +namespace OrchardCore.Spatial.Drivers; + +public sealed class GeoPointFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class GeoPointFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("GeoPointFieldSettings_Edit", model => { - return Initialize("GeoPointFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new GeoPointFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new GeoPointFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return Edit(partFieldDefinition, context); - } + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Fields/GeoPointField.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Fields/GeoPointField.cs index a7275250ca3..7d028596197 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Fields/GeoPointField.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Fields/GeoPointField.cs @@ -1,11 +1,10 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Spatial.Fields +namespace OrchardCore.Spatial.Fields; + +public class GeoPointField : ContentField { - public class GeoPointField : ContentField - { - public decimal? Latitude { get; set; } + public decimal? Latitude { get; set; } - public decimal? Longitude { get; set; } - } + public decimal? Longitude { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Indexing/GeoPointFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Indexing/GeoPointFieldIndexHandler.cs index 9e66d3f269c..8e9926304fc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Indexing/GeoPointFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Indexing/GeoPointFieldIndexHandler.cs @@ -2,33 +2,32 @@ using OrchardCore.Indexing; using OrchardCore.Spatial.Fields; -namespace OrchardCore.Spatial.Indexing +namespace OrchardCore.Spatial.Indexing; + +public class GeoPointFieldIndexHandler : ContentFieldIndexHandler { - public class GeoPointFieldIndexHandler : ContentFieldIndexHandler + public override Task BuildIndexAsync(GeoPointField field, BuildFieldIndexContext context) { - public override Task BuildIndexAsync(GeoPointField field, BuildFieldIndexContext context) - { - var options = context.Settings.ToOptions(); - DocumentIndex.GeoPoint value = null; + var options = context.Settings.ToOptions(); + DocumentIndex.GeoPoint value = null; - if (field.Longitude != null && field.Latitude != null) + if (field.Longitude != null && field.Latitude != null) + { + value = new DocumentIndex.GeoPoint { - value = new DocumentIndex.GeoPoint - { - Longitude = (decimal)field.Longitude, - Latitude = (decimal)field.Latitude - }; - } + Longitude = (decimal)field.Longitude, + Latitude = (decimal)field.Latitude + }; + } - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key, value, options); - } + foreach (var key in context.Keys) + { + context.DocumentIndex.Set(key, value, options); + } - // Also index as "Location" to be able to search on multiple Content Types - context.DocumentIndex.Set("Location", value, DocumentIndexOptions.Store); + // Also index as "Location" to be able to search on multiple Content Types + context.DocumentIndex.Set("Location", value, DocumentIndexOptions.Store); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/ResourceManagementOptionsConfiguration.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/ResourceManagementOptionsConfiguration.cs index 0d75d6d5555..8be8e614764 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/ResourceManagementOptionsConfiguration.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/ResourceManagementOptionsConfiguration.cs @@ -1,34 +1,33 @@ using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement; -namespace OrchardCore.Spatial +namespace OrchardCore.Spatial; + +public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions { - public sealed class ResourceManagementOptionsConfiguration : IConfigureOptions - { - private static readonly ResourceManifest _manifest; + private static readonly ResourceManifest _manifest; - static ResourceManagementOptionsConfiguration() - { - _manifest = new ResourceManifest(); + static ResourceManagementOptionsConfiguration() + { + _manifest = new ResourceManifest(); - _manifest - .DefineScript("leaflet") - .SetUrl("/OrchardCore.Spatial/Scripts/leaflet/leaflet.js", "/OrchardCore.Spatial/Scripts/leaflet/leaflet-src.js") - .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js", "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet-src.js") - .SetCdnIntegrity("sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH", "sha384-4aETf8z71hiSsoK0xYsa5JtiJHfL3h7uMAsZ2QYOLvcySDL/cEDfdLt0SaBypTQZ") - .SetVersion("1.9.4"); + _manifest + .DefineScript("leaflet") + .SetUrl("/OrchardCore.Spatial/Scripts/leaflet/leaflet.js", "/OrchardCore.Spatial/Scripts/leaflet/leaflet-src.js") + .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js", "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet-src.js") + .SetCdnIntegrity("sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH", "sha384-4aETf8z71hiSsoK0xYsa5JtiJHfL3h7uMAsZ2QYOLvcySDL/cEDfdLt0SaBypTQZ") + .SetVersion("1.9.4"); - _manifest - .DefineStyle("leaflet") - .SetUrl("/OrchardCore.Spatial/Styles/leaflet.min.css", "/OrchardCore.Spatial/Styles/leaflet.css") - .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css", "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css") - .SetCdnIntegrity("sha384-c6Rcwz4e4CITMbu/NBmnNS8yN2sC3cUElMEMfP3vqqKFp7GOYaaBBCqmaWBjmkjb", "sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H") - .SetVersion("1.9.4"); - } + _manifest + .DefineStyle("leaflet") + .SetUrl("/OrchardCore.Spatial/Styles/leaflet.min.css", "/OrchardCore.Spatial/Styles/leaflet.css") + .SetCdn("https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css", "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css") + .SetCdnIntegrity("sha384-c6Rcwz4e4CITMbu/NBmnNS8yN2sC3cUElMEMfP3vqqKFp7GOYaaBBCqmaWBjmkjb", "sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H") + .SetVersion("1.9.4"); + } - public void Configure(ResourceManagementOptions options) - { - options.ResourceManifests.Add(_manifest); - } + public void Configure(ResourceManagementOptions options) + { + options.ResourceManifests.Add(_manifest); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Settings/GeoPointFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Settings/GeoPointFieldSettings.cs index 6b1b979442c..978334109b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Settings/GeoPointFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Settings/GeoPointFieldSettings.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Spatial.Settings +namespace OrchardCore.Spatial.Settings; + +public class GeoPointFieldSettings { - public class GeoPointFieldSettings - { - public string Hint { get; set; } - public bool Required { get; set; } - } + public string Hint { get; set; } + public bool Required { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/Startup.cs index f75c6438195..9a5d11a65f7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/Startup.cs @@ -13,29 +13,28 @@ using OrchardCore.Spatial.Indexing; using OrchardCore.Spatial.ViewModels; -namespace OrchardCore.Spatial +namespace OrchardCore.Spatial; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddTransient, ResourceManagementOptionsConfiguration>(); + services.AddTransient, ResourceManagementOptionsConfiguration>(); - // Coordinate Field - services.AddContentField() - .UseDisplayDriver() - .AddHandler(); + // Coordinate Field + services.AddContentField() + .UseDisplayDriver() + .AddHandler(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // Registering both field types and shape types are necessary as they can - // be accessed from inner properties. - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }); - } + // Registering both field types and shape types are necessary as they can + // be accessed from inner properties. + services.Configure(o => + { + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/DisplayGeoPointFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/DisplayGeoPointFieldViewModel.cs index ed99a8cf565..7a568565dfd 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/DisplayGeoPointFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/DisplayGeoPointFieldViewModel.cs @@ -2,12 +2,11 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Spatial.Fields; -namespace OrchardCore.Spatial.ViewModels +namespace OrchardCore.Spatial.ViewModels; + +public class DisplayGeoPointFieldViewModel { - public class DisplayGeoPointFieldViewModel - { - public GeoPointField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public GeoPointField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/EditGeoPointFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/EditGeoPointFieldViewModel.cs index be34f7b1f81..8daabf2f229 100644 --- a/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/EditGeoPointFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Spatial/ViewModels/EditGeoPointFieldViewModel.cs @@ -3,20 +3,19 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Spatial.Fields; -namespace OrchardCore.Spatial.ViewModels +namespace OrchardCore.Spatial.ViewModels; + +public class EditGeoPointFieldViewModel { - public class EditGeoPointFieldViewModel - { - public string Latitude { get; set; } - public string Longitude { get; set; } + public string Latitude { get; set; } + public string Longitude { get; set; } - [BindNever] - public GeoPointField Field { get; set; } + [BindNever] + public GeoPointField Field { get; set; } - [BindNever] - public ContentPart Part { get; set; } + [BindNever] + public ContentPart Part { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/AdminMenu.cs index 539df6231c3..f37bf24fb42 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/AdminMenu.cs @@ -4,44 +4,43 @@ using OrchardCore.Navigation; using OrchardCore.Taxonomies.Settings; -namespace OrchardCore.Taxonomies +namespace OrchardCore.Taxonomies; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + private static readonly RouteValueDictionary _routeValues = new() { - private static readonly RouteValueDictionary _routeValues = new() - { - { "area", "OrchardCore.Settings" }, - { "groupId", TaxonomyContentsAdminListSettingsDisplayDriver.GroupId }, - }; + { "area", "OrchardCore.Settings" }, + { "groupId", TaxonomyContentsAdminListSettingsDisplayDriver.GroupId }, + }; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public AdminMenu(IStringLocalizer localizer) - { - S = localizer; - } + public AdminMenu(IStringLocalizer localizer) + { + S = localizer; + } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } + return Task.CompletedTask; + } - builder - .Add(S["Configuration"], configuration => configuration - .Add(S["Settings"], "1", settings => settings - .Add(S["Taxonomy Filters"], S["Taxonomy Filters"].PrefixPosition(), filters => filters - .AddClass("taxonomyfilters") - .Id("taxonomyfilters") - .Permission(Permissions.ManageTaxonomies) - .Action("Index", "Admin", _routeValues) - .LocalNav() - ) + builder + .Add(S["Configuration"], configuration => configuration + .Add(S["Settings"], "1", settings => settings + .Add(S["Taxonomy Filters"], S["Taxonomy Filters"].PrefixPosition(), filters => filters + .AddClass("taxonomyfilters") + .Id("taxonomyfilters") + .Permission(Permissions.ManageTaxonomies) + .Action("Index", "Admin", _routeValues) + .LocalNav() ) - ); + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/AdminController.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/AdminController.cs index c05eb7f6b5b..32183d7afc3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/AdminController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/AdminController.cs @@ -15,378 +15,377 @@ using OrchardCore.Taxonomies.Models; using YesSql; -namespace OrchardCore.Taxonomies.Controllers +namespace OrchardCore.Taxonomies.Controllers; + +[Admin] +public class AdminController : Controller { - [Admin] - public class AdminController : Controller + private readonly IContentManager _contentManager; + private readonly IAuthorizationService _authorizationService; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ISession _session; + private readonly INotifier _notifier; + private readonly IUpdateModelAccessor _updateModelAccessor; + + protected readonly IHtmlLocalizer H; + + public AdminController( + ISession session, + IContentManager contentManager, + IAuthorizationService authorizationService, + IContentItemDisplayManager contentItemDisplayManager, + IContentDefinitionManager contentDefinitionManager, + INotifier notifier, + IHtmlLocalizer localizer, + IUpdateModelAccessor updateModelAccessor) { - private readonly IContentManager _contentManager; - private readonly IAuthorizationService _authorizationService; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly ISession _session; - private readonly INotifier _notifier; - private readonly IUpdateModelAccessor _updateModelAccessor; - - protected readonly IHtmlLocalizer H; - - public AdminController( - ISession session, - IContentManager contentManager, - IAuthorizationService authorizationService, - IContentItemDisplayManager contentItemDisplayManager, - IContentDefinitionManager contentDefinitionManager, - INotifier notifier, - IHtmlLocalizer localizer, - IUpdateModelAccessor updateModelAccessor) - { - _contentManager = contentManager; - _authorizationService = authorizationService; - _contentItemDisplayManager = contentItemDisplayManager; - _contentDefinitionManager = contentDefinitionManager; - _session = session; - _notifier = notifier; - _updateModelAccessor = updateModelAccessor; - H = localizer; - } - - [Admin("Taxonomies/Create/{id}", "Taxonomies.Create")] - public async Task Create(string id, string taxonomyContentItemId, string taxonomyItemId) - { - if (string.IsNullOrWhiteSpace(id)) - { - return NotFound(); - } + _contentManager = contentManager; + _authorizationService = authorizationService; + _contentItemDisplayManager = contentItemDisplayManager; + _contentDefinitionManager = contentDefinitionManager; + _session = session; + _notifier = notifier; + _updateModelAccessor = updateModelAccessor; + H = localizer; + } - if (await _contentDefinitionManager.GetTypeDefinitionAsync(id) == null) - { - return NotFound(); - } + [Admin("Taxonomies/Create/{id}", "Taxonomies.Create")] + public async Task Create(string id, string taxonomyContentItemId, string taxonomyItemId) + { + if (string.IsNullOrWhiteSpace(id)) + { + return NotFound(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) - { - return Forbid(); - } + if (await _contentDefinitionManager.GetTypeDefinitionAsync(id) == null) + { + return NotFound(); + } - var contentItem = await _contentManager.NewAsync(id); - contentItem.Weld(); - contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) + { + return Forbid(); + } - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); + var contentItem = await _contentManager.NewAsync(id); + contentItem.Weld(); + contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); - model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; - model.Properties["TaxonomyItemId"] = taxonomyItemId; + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); - return View(model); + model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; + model.Properties["TaxonomyItemId"] = taxonomyItemId; + + return View(model); + } + + [HttpPost] + [ActionName("Create")] + public async Task CreatePost(string id, string taxonomyContentItemId, string taxonomyItemId) + { + if (string.IsNullOrWhiteSpace(id)) + { + return NotFound(); } - [HttpPost] - [ActionName("Create")] - public async Task CreatePost(string id, string taxonomyContentItemId, string taxonomyItemId) + if (await _contentDefinitionManager.GetTypeDefinitionAsync(id) == null) { - if (string.IsNullOrWhiteSpace(id)) - { - return NotFound(); - } + return NotFound(); + } - if (await _contentDefinitionManager.GetTypeDefinitionAsync(id) == null) - { - return NotFound(); - } + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) + { + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) - { - return Forbid(); - } + ContentItem taxonomy; - ContentItem taxonomy; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); + if (!contentTypeDefinition.IsDraftable()) + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); + } + else + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); + } - if (!contentTypeDefinition.IsDraftable()) - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); - } - else - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); - } + if (taxonomy == null) + { + return NotFound(); + } - if (taxonomy == null) - { - return NotFound(); - } + var contentItem = await _contentManager.NewAsync(id); + contentItem.Weld(); + contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); - var contentItem = await _contentManager.NewAsync(id); - contentItem.Weld(); - contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, true); + if (!ModelState.IsValid) + { + model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; + model.Properties["TaxonomyItemId"] = taxonomyItemId; - if (!ModelState.IsValid) - { - model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; - model.Properties["TaxonomyItemId"] = taxonomyItemId; + return View(model); + } - return View(model); - } + if (taxonomyItemId == null) + { + // Use the taxonomy as the parent if no target is specified. + taxonomy.Alter(part => part.Terms.Add(contentItem)); + } + else + { + // Look for the target taxonomy item in the hierarchy. + var parentTaxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); - if (taxonomyItemId == null) + // Couldn't find targeted taxonomy item. + if (parentTaxonomyItem == null) { - // Use the taxonomy as the parent if no target is specified. - taxonomy.Alter(part => part.Terms.Add(contentItem)); + return NotFound(); } - else - { - // Look for the target taxonomy item in the hierarchy. - var parentTaxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); - // Couldn't find targeted taxonomy item. - if (parentTaxonomyItem == null) - { - return NotFound(); - } + var taxonomyItems = (JsonArray)parentTaxonomyItem?["Terms"]; - var taxonomyItems = (JsonArray)parentTaxonomyItem?["Terms"]; + if (taxonomyItems == null) + { + parentTaxonomyItem["Terms"] = taxonomyItems = []; + } - if (taxonomyItems == null) - { - parentTaxonomyItem["Terms"] = taxonomyItems = []; - } + taxonomyItems.Add(JObject.FromObject(contentItem)); + } - taxonomyItems.Add(JObject.FromObject(contentItem)); - } + await _session.SaveAsync(taxonomy); - await _session.SaveAsync(taxonomy); + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + } - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + [Admin("Taxonomies/Edit/{taxonomyContentItemId}/{taxonomyItemId}", "Taxonomies.Create")] + public async Task Edit(string taxonomyContentItemId, string taxonomyItemId) + { + if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) + { + return NotFound(); } - [Admin("Taxonomies/Edit/{taxonomyContentItemId}/{taxonomyItemId}", "Taxonomies.Create")] - public async Task Edit(string taxonomyContentItemId, string taxonomyItemId) - { - if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) - { - return NotFound(); - } + var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); - var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); + if (taxonomy == null) + { + return NotFound(); + } - if (taxonomy == null) - { - return NotFound(); - } + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies, taxonomy)) + { + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies, taxonomy)) - { - return Forbid(); - } + // Look for the target taxonomy item in the hierarchy. + var taxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); - // Look for the target taxonomy item in the hierarchy. - var taxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); + // Couldn't find targeted taxonomy item. + if (taxonomyItem == null) + { + return NotFound(); + } - // Couldn't find targeted taxonomy item. - if (taxonomyItem == null) - { - return NotFound(); - } + var contentItem = taxonomyItem.ToObject(); + contentItem.Weld(); + contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); - var contentItem = taxonomyItem.ToObject(); - contentItem.Weld(); - contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); + var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - var model = await _contentItemDisplayManager.BuildEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; + model.Properties["TaxonomyItemId"] = taxonomyItemId; - model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; - model.Properties["TaxonomyItemId"] = taxonomyItemId; + return View(model); + } - return View(model); + [HttpPost] + [ActionName("Edit")] + public async Task EditPost(string taxonomyContentItemId, string taxonomyItemId) + { + if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) + { + return NotFound(); } - [HttpPost] - [ActionName("Edit")] - public async Task EditPost(string taxonomyContentItemId, string taxonomyItemId) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) { - if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) - { - return NotFound(); - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) - { - return Forbid(); - } + ContentItem taxonomy; - ContentItem taxonomy; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); + if (!contentTypeDefinition.IsDraftable()) + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); + } + else + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); + } - if (!contentTypeDefinition.IsDraftable()) - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); - } - else - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); - } + if (taxonomy == null) + { + return NotFound(); + } - if (taxonomy == null) - { - return NotFound(); - } + // Look for the target taxonomy item in the hierarchy. + var taxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); - // Look for the target taxonomy item in the hierarchy. - var taxonomyItem = FindTaxonomyItem((JsonObject)taxonomy.As().Content, taxonomyItemId); + // Couldn't find targeted taxonomy item. + if (taxonomyItem == null) + { + return NotFound(); + } - // Couldn't find targeted taxonomy item. - if (taxonomyItem == null) - { - return NotFound(); - } + var existing = taxonomyItem.ToObject(); - var existing = taxonomyItem.ToObject(); + // Create a new item to take into account the current type definition. + var contentItem = await _contentManager.NewAsync(existing.ContentType); - // Create a new item to take into account the current type definition. - var contentItem = await _contentManager.NewAsync(existing.ContentType); + contentItem.ContentItemId = existing.ContentItemId; + contentItem.Merge(existing); + contentItem.Weld(); + contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); - contentItem.ContentItemId = existing.ContentItemId; - contentItem.Merge(existing); - contentItem.Weld(); - contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); + var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); - var model = await _contentItemDisplayManager.UpdateEditorAsync(contentItem, _updateModelAccessor.ModelUpdater, false); + if (!ModelState.IsValid) + { + model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; + model.Properties["TaxonomyItemId"] = taxonomyItemId; - if (!ModelState.IsValid) - { - model.Properties["TaxonomyContentItemId"] = taxonomyContentItemId; - model.Properties["TaxonomyItemId"] = taxonomyItemId; + return View(model); + } - return View(model); - } + taxonomyItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings + { + MergeArrayHandling = MergeArrayHandling.Replace, + MergeNullValueHandling = MergeNullValueHandling.Merge + }); - taxonomyItem.Merge((JsonObject)contentItem.Content, new JsonMergeSettings - { - MergeArrayHandling = MergeArrayHandling.Replace, - MergeNullValueHandling = MergeNullValueHandling.Merge - }); + // Merge doesn't copy the properties. + taxonomyItem[nameof(ContentItem.DisplayText)] = contentItem.DisplayText; - // Merge doesn't copy the properties. - taxonomyItem[nameof(ContentItem.DisplayText)] = contentItem.DisplayText; + await _session.SaveAsync(taxonomy); - await _session.SaveAsync(taxonomy); + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + } - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + [HttpPost] + [Admin("Taxonomies/Delete/{taxonomyContentItemId}/{taxonomyItemId}", "Taxonomies.Delete")] + public async Task Delete(string taxonomyContentItemId, string taxonomyItemId) + { + if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) + { + return NotFound(); } - [HttpPost] - [Admin("Taxonomies/Delete/{taxonomyContentItemId}/{taxonomyItemId}", "Taxonomies.Delete")] - public async Task Delete(string taxonomyContentItemId, string taxonomyItemId) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) { - if (string.IsNullOrWhiteSpace(taxonomyContentItemId) || string.IsNullOrWhiteSpace(taxonomyItemId)) - { - return NotFound(); - } + return Forbid(); + } - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) - { - return Forbid(); - } + ContentItem taxonomy; - ContentItem taxonomy; + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); + if (!contentTypeDefinition.IsDraftable()) + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); + } + else + { + taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); + } - if (!contentTypeDefinition.IsDraftable()) - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.Latest); - } - else - { - taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, VersionOptions.DraftRequired); - } + if (taxonomy == null) + { + return NotFound(); + } - if (taxonomy == null) - { - return NotFound(); - } + // Look for the target taxonomy item in the hierarchy. + var content = (JsonObject)taxonomy.As().Content; - // Look for the target taxonomy item in the hierarchy. - var content = (JsonObject)taxonomy.As().Content; + RemoveTaxonomyItem(content, taxonomyItemId); - RemoveTaxonomyItem(content, taxonomyItemId); + var updatedPart = content.ToObject(); - var updatedPart = content.ToObject(); + if (updatedPart == null) + { + return NotFound(); + } - if (updatedPart == null) - { - return NotFound(); - } + taxonomy.Apply(updatedPart); - taxonomy.Apply(updatedPart); + await _session.SaveAsync(taxonomy); - await _session.SaveAsync(taxonomy); + await _notifier.SuccessAsync(H["Taxonomy item deleted successfully."]); - await _notifier.SuccessAsync(H["Taxonomy item deleted successfully."]); + return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + } - return RedirectToAction(nameof(Edit), "Admin", new { area = "OrchardCore.Contents", contentItemId = taxonomyContentItemId }); + private static JsonObject FindTaxonomyItem(JsonObject contentItem, string taxonomyItemId) + { + if (contentItem["ContentItemId"]?.Value() == taxonomyItemId) + { + return contentItem; } - private static JsonObject FindTaxonomyItem(JsonObject contentItem, string taxonomyItemId) + if (!contentItem.TryGetPropertyValue("Terms", out var terms) || terms is not JsonArray taxonomyItems) { - if (contentItem["ContentItemId"]?.Value() == taxonomyItemId) - { - return contentItem; - } + return null; + } - if (!contentItem.TryGetPropertyValue("Terms", out var terms) || terms is not JsonArray taxonomyItems) + JsonObject result; + foreach (var taxonomyItem in taxonomyItems.Cast()) + { + // Search in inner taxonomy items. + result = FindTaxonomyItem(taxonomyItem, taxonomyItemId); + + if (result != null) { - return null; + return result; } + } - JsonObject result; - foreach (var taxonomyItem in taxonomyItems.Cast()) - { - // Search in inner taxonomy items. - result = FindTaxonomyItem(taxonomyItem, taxonomyItemId); + return null; + } - if (result != null) - { - return result; - } - } + private static bool RemoveTaxonomyItem(JsonObject contentItem, string taxonomyItemToRemove) + { + if (contentItem == null) + { + return false; + } - return null; + if (contentItem["ContentItemId"]?.Value() == taxonomyItemToRemove) + { + return true; } - private static bool RemoveTaxonomyItem(JsonObject contentItem, string taxonomyItemToRemove) + if (!contentItem.TryGetPropertyValue("Terms", out var terms) || terms is not JsonArray taxonomyItems) { - if (contentItem == null) - { - return false; - } + return false; + } - if (contentItem["ContentItemId"]?.Value() == taxonomyItemToRemove) + for (var i = taxonomyItems.Count - 1; i >= 0; i--) + { + var taxonomyItem = taxonomyItems[i] as JsonObject; + if (RemoveTaxonomyItem(taxonomyItem, taxonomyItemToRemove)) { - return true; - } + taxonomyItems.RemoveAt(i); - if (!contentItem.TryGetPropertyValue("Terms", out var terms) || terms is not JsonArray taxonomyItems) - { return false; } - - for (var i = taxonomyItems.Count - 1; i >= 0; i--) - { - var taxonomyItem = taxonomyItems[i] as JsonObject; - if (RemoveTaxonomyItem(taxonomyItem, taxonomyItemToRemove)) - { - taxonomyItems.RemoveAt(i); - - return false; - } - } - - return false; } + + return false; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/TagController.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/TagController.cs index a598ea8e370..35eba8ccc99 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/TagController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Controllers/TagController.cs @@ -15,98 +15,97 @@ using OrchardCore.Taxonomies.ViewModels; using YesSql; -namespace OrchardCore.Taxonomies.Controllers +namespace OrchardCore.Taxonomies.Controllers; + +[Admin] +public class TagController : Controller, IUpdateModel { - [Admin] - public class TagController : Controller, IUpdateModel + private readonly IContentManager _contentManager; + private readonly IAuthorizationService _authorizationService; + private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly IEnumerable _contentHandlers; + private readonly ISession _session; + private readonly ILogger _logger; + + public TagController( + IContentManager contentManager, + IAuthorizationService authorizationService, + IContentDefinitionManager contentDefinitionManager, + IEnumerable contentHandlers, + ISession session, + ILogger logger) + { + _contentManager = contentManager; + _authorizationService = authorizationService; + _contentDefinitionManager = contentDefinitionManager; + _contentHandlers = contentHandlers; + _session = session; + _logger = logger; + } + + [HttpPost] + [ActionName("Create")] + public async Task CreatePost(string taxonomyContentItemId, string displayText) { - private readonly IContentManager _contentManager; - private readonly IAuthorizationService _authorizationService; - private readonly IContentDefinitionManager _contentDefinitionManager; - private readonly IEnumerable _contentHandlers; - private readonly ISession _session; - private readonly ILogger _logger; - - public TagController( - IContentManager contentManager, - IAuthorizationService authorizationService, - IContentDefinitionManager contentDefinitionManager, - IEnumerable contentHandlers, - ISession session, - ILogger logger) + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) { - _contentManager = contentManager; - _authorizationService = authorizationService; - _contentDefinitionManager = contentDefinitionManager; - _contentHandlers = contentHandlers; - _session = session; - _logger = logger; + return Unauthorized(); } - [HttpPost] - [ActionName("Create")] - public async Task CreatePost(string taxonomyContentItemId, string displayText) + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); + var versionOption = VersionOptions.Latest; + + if (contentTypeDefinition.IsDraftable()) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTaxonomies)) - { - return Unauthorized(); - } - - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync("Taxonomy"); - var versionOption = VersionOptions.Latest; - - if (contentTypeDefinition.IsDraftable()) - { - versionOption = VersionOptions.DraftRequired; - } - - var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, versionOption); - - if (taxonomy == null) - { - return NotFound(); - } - - var part = taxonomy.As(); - - // Create tag term but only run content handlers not content item display manager update editor. - // This creates empty parts, if parts are attached to the tag term, with empty data. - // But still generates valid autoroute paths from the handler. - var contentItem = await _contentManager.NewAsync(part.TermContentType); - contentItem.DisplayText = displayText; - contentItem.Weld(); - contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); - - var updateContentContext = new UpdateContentContext(contentItem); - - await _contentHandlers.InvokeAsync((handler, updateContentContext) => handler.UpdatingAsync(updateContentContext), updateContentContext, _logger); - await _contentHandlers.Reverse().InvokeAsync((handler, updateContentContext) => handler.UpdatedAsync(updateContentContext), updateContentContext, _logger); - - if (!ModelState.IsValid) - { - return BadRequest(); - } - - // Tag terms are always added to the root taxonomy element. - taxonomy.Alter(part => part.Terms.Add(contentItem)); - - // Auto publish draftable taxonomies when creating a new tag term. - if (contentTypeDefinition.IsDraftable()) - { - await _contentManager.PublishAsync(taxonomy); - } - else - { - await _session.SaveAsync(taxonomy); - } - - var viewModel = new CreatedTagViewModel - { - ContentItemId = contentItem.ContentItemId, - DisplayText = contentItem.DisplayText - }; - - return Ok(viewModel); + versionOption = VersionOptions.DraftRequired; } + + var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId, versionOption); + + if (taxonomy == null) + { + return NotFound(); + } + + var part = taxonomy.As(); + + // Create tag term but only run content handlers not content item display manager update editor. + // This creates empty parts, if parts are attached to the tag term, with empty data. + // But still generates valid autoroute paths from the handler. + var contentItem = await _contentManager.NewAsync(part.TermContentType); + contentItem.DisplayText = displayText; + contentItem.Weld(); + contentItem.Alter(t => t.TaxonomyContentItemId = taxonomyContentItemId); + + var updateContentContext = new UpdateContentContext(contentItem); + + await _contentHandlers.InvokeAsync((handler, updateContentContext) => handler.UpdatingAsync(updateContentContext), updateContentContext, _logger); + await _contentHandlers.Reverse().InvokeAsync((handler, updateContentContext) => handler.UpdatedAsync(updateContentContext), updateContentContext, _logger); + + if (!ModelState.IsValid) + { + return BadRequest(); + } + + // Tag terms are always added to the root taxonomy element. + taxonomy.Alter(part => part.Terms.Add(contentItem)); + + // Auto publish draftable taxonomies when creating a new tag term. + if (contentTypeDefinition.IsDraftable()) + { + await _contentManager.PublishAsync(taxonomy); + } + else + { + await _session.SaveAsync(taxonomy); + } + + var viewModel = new CreatedTagViewModel + { + ContentItemId = contentItem.ContentItemId, + DisplayText = contentItem.DisplayText + }; + + return Ok(viewModel); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyContentsAdminListDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyContentsAdminListDisplayDriver.cs index 2d7c9984273..3cf06073f65 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyContentsAdminListDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyContentsAdminListDisplayDriver.cs @@ -18,163 +18,162 @@ using OrchardCore.Taxonomies.Settings; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public sealed class TaxonomyContentsAdminListDisplayDriver : DisplayDriver { - public sealed class TaxonomyContentsAdminListDisplayDriver : DisplayDriver - { - private const string LevelPadding = "\xA0\xA0"; + private const string LevelPadding = "\xA0\xA0"; - private readonly ISiteService _siteService; - private readonly IContentManager _contentManager; - private readonly IContentDefinitionManager _contentDefinitionManager; + private readonly ISiteService _siteService; + private readonly IContentManager _contentManager; + private readonly IContentDefinitionManager _contentDefinitionManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; + + public TaxonomyContentsAdminListDisplayDriver( + ISiteService siteService, + IContentManager contentManager, + IContentDefinitionManager contentDefinitionManager, + IStringLocalizer stringLocalizer) + { + _siteService = siteService; + _contentManager = contentManager; + _contentDefinitionManager = contentDefinitionManager; + S = stringLocalizer; + } + + public override async Task EditAsync(ContentOptionsViewModel model, BuildEditorContext context) + { + var settings = await _siteService.GetSettingsAsync(); - public TaxonomyContentsAdminListDisplayDriver( - ISiteService siteService, - IContentManager contentManager, - IContentDefinitionManager contentDefinitionManager, - IStringLocalizer stringLocalizer) + if (settings.TaxonomyContentItemIds.Length == 0) { - _siteService = siteService; - _contentManager = contentManager; - _contentDefinitionManager = contentDefinitionManager; - S = stringLocalizer; + return null; } - public override async Task EditAsync(ContentOptionsViewModel model, BuildEditorContext context) + var taxonomyContentItemIds = settings.TaxonomyContentItemIds; + if (!string.IsNullOrEmpty(model.SelectedContentType)) { - var settings = await _siteService.GetSettingsAsync(); + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(model.SelectedContentType); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TaxonomyField))); + var fieldTaxonomyContentItemIds = fieldDefinitions.Select(x => x.GetSettings().TaxonomyContentItemId); + taxonomyContentItemIds = taxonomyContentItemIds.Intersect(fieldTaxonomyContentItemIds).ToArray(); - if (settings.TaxonomyContentItemIds.Length == 0) + if (taxonomyContentItemIds.Length == 0) { return null; } + } - var taxonomyContentItemIds = settings.TaxonomyContentItemIds; - if (!string.IsNullOrEmpty(model.SelectedContentType)) - { - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(model.SelectedContentType); - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TaxonomyField))); - var fieldTaxonomyContentItemIds = fieldDefinitions.Select(x => x.GetSettings().TaxonomyContentItemId); - taxonomyContentItemIds = taxonomyContentItemIds.Intersect(fieldTaxonomyContentItemIds).ToArray(); + var results = new List(); + var taxonomies = await _contentManager.GetAsync(taxonomyContentItemIds); - if (taxonomyContentItemIds.Length == 0) + var position = 5; + foreach (var taxonomy in taxonomies) + { + results.Add( + Initialize("ContentsAdminListTaxonomyFilter", m => { - return null; - } - } - - var results = new List(); - var taxonomies = await _contentManager.GetAsync(taxonomyContentItemIds); - - var position = 5; - foreach (var taxonomy in taxonomies) - { - results.Add( - Initialize("ContentsAdminListTaxonomyFilter", m => - { - using var sb = ZString.CreateStringBuilder(); - var termEntries = new List(); - PopulateTermEntries(termEntries, taxonomy.As().Terms, 0); - var terms = new List - { - new() - { - Text = S["Clear filter"], - Value = string.Empty, - }, - new() - { - Text = S["Show all"], - Value = "Taxonomy:" + taxonomy.ContentItemId, - } - }; - - foreach (var term in termEntries) + using var sb = ZString.CreateStringBuilder(); + var termEntries = new List(); + PopulateTermEntries(termEntries, taxonomy.As().Terms, 0); + var terms = new List { - sb.Clear(); - for (var l = 0; l < term.Level; l++) + new() { - sb.Append(LevelPadding); - } - sb.Append(term.DisplayText); - var item = new SelectListItem + Text = S["Clear filter"], + Value = string.Empty, + }, + new() { - Text = sb.ToString(), - Value = "Term:" + term.ContentItemId, - }; - terms.Add(item); + Text = S["Show all"], + Value = "Taxonomy:" + taxonomy.ContentItemId, + } + }; + + foreach (var term in termEntries) + { + sb.Clear(); + for (var l = 0; l < term.Level; l++) + { + sb.Append(LevelPadding); } + sb.Append(term.DisplayText); + var item = new SelectListItem + { + Text = sb.ToString(), + Value = "Term:" + term.ContentItemId, + }; + terms.Add(item); + } + + m.DisplayText = taxonomy.DisplayText; + m.Taxonomies = terms; + }) + .Location("Actions:40." + position) + .Prefix("Taxonomy" + taxonomy.ContentItemId) + ); + + position += 5; + } + + if (results.Count > 0) + { + return Combine(results); + } - m.DisplayText = taxonomy.DisplayText; - m.Taxonomies = terms; - }) - .Location("Actions:40." + position) - .Prefix("Taxonomy" + taxonomy.ContentItemId) - ); + return null; + } - position += 5; - } + public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + { + var settings = await _siteService.GetSettingsAsync(); + foreach (var contentItemId in settings.TaxonomyContentItemIds) + { + var viewModel = new TaxonomyContentsAdminFilterViewModel(); + await context.Updater.TryUpdateModelAsync(viewModel, "Taxonomy" + contentItemId); - if (results.Count > 0) + if (!string.IsNullOrEmpty(viewModel.SelectedContentItemId)) { - return Combine(results); + model.RouteValues.TryAdd("Taxonomy" + contentItemId + ".SelectedContentItemId", viewModel.SelectedContentItemId); } - - return null; } - public override async Task UpdateAsync(ContentOptionsViewModel model, UpdateEditorContext context) + return await EditAsync(model, context); + } + + private static void PopulateTermEntries(List termEntries, IEnumerable contentItems, int level) + { + foreach (var contentItem in contentItems) { - var settings = await _siteService.GetSettingsAsync(); - foreach (var contentItemId in settings.TaxonomyContentItemIds) - { - var viewModel = new TaxonomyContentsAdminFilterViewModel(); - await context.Updater.TryUpdateModelAsync(viewModel, "Taxonomy" + contentItemId); + var children = Array.Empty(); - if (!string.IsNullOrEmpty(viewModel.SelectedContentItemId)) - { - model.RouteValues.TryAdd("Taxonomy" + contentItemId + ".SelectedContentItemId", viewModel.SelectedContentItemId); - } + if (((JsonObject)contentItem.Content)["Terms"] is JsonArray termsArray) + { + children = termsArray.ToObject(); } - return await EditAsync(model, context); - } - - private static void PopulateTermEntries(List termEntries, IEnumerable contentItems, int level) - { - foreach (var contentItem in contentItems) + var termEntry = new FilterTermEntry { - var children = Array.Empty(); - - if (((JsonObject)contentItem.Content)["Terms"] is JsonArray termsArray) - { - children = termsArray.ToObject(); - } - - var termEntry = new FilterTermEntry - { - DisplayText = contentItem.DisplayText, - ContentItemId = contentItem.ContentItemId, - Level = level - }; + DisplayText = contentItem.DisplayText, + ContentItemId = contentItem.ContentItemId, + Level = level + }; - termEntries.Add(termEntry); + termEntries.Add(termEntry); - if (children.Length > 0) - { - PopulateTermEntries(termEntries, children, level + 1); - } + if (children.Length > 0) + { + PopulateTermEntries(termEntries, children, level + 1); } } } +} - public class FilterTermEntry - { - public string DisplayText { get; set; } - public string ContentItemId { get; set; } - public int Level { get; set; } - } +public class FilterTermEntry +{ + public string DisplayText { get; set; } + public string ContentItemId { get; set; } + public int Level { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDisplayDriver.cs index 5d3c82b1b46..d776427569e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDisplayDriver.cs @@ -13,79 +13,78 @@ using OrchardCore.Taxonomies.Settings; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public sealed class TaxonomyFieldDisplayDriver : ContentFieldDisplayDriver { - public sealed class TaxonomyFieldDisplayDriver : ContentFieldDisplayDriver - { - private readonly IContentManager _contentManager; + private readonly IContentManager _contentManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public TaxonomyFieldDisplayDriver( - IContentManager contentManager, - IStringLocalizer localizer) - { - _contentManager = contentManager; - S = localizer; - } + public TaxonomyFieldDisplayDriver( + IContentManager contentManager, + IStringLocalizer localizer) + { + _contentManager = contentManager; + S = localizer; + } - public override IDisplayResult Display(TaxonomyField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(TaxonomyField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }).Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }).Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(TaxonomyField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(TaxonomyField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => + var settings = context.PartFieldDefinition.GetSettings(); + model.Taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); + + if (model.Taxonomy != null) { - var settings = context.PartFieldDefinition.GetSettings(); - model.Taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); - - if (model.Taxonomy != null) - { - var termEntries = new List(); - TaxonomyFieldDriverHelper.PopulateTermEntries(termEntries, field, model.Taxonomy.As().Terms, 0); - - model.TermEntries = termEntries; - model.UniqueValue = termEntries.FirstOrDefault(x => x.Selected)?.ContentItemId; - } - - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + var termEntries = new List(); + TaxonomyFieldDriverHelper.PopulateTermEntries(termEntries, field, model.Taxonomy.As().Terms, 0); - public override async Task UpdateAsync(TaxonomyField field, UpdateFieldEditorContext context) - { - var model = new EditTaxonomyFieldViewModel(); + model.TermEntries = termEntries; + model.UniqueValue = termEntries.FirstOrDefault(x => x.Selected)?.ContentItemId; + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - var settings = context.PartFieldDefinition.GetSettings(); + public override async Task UpdateAsync(TaxonomyField field, UpdateFieldEditorContext context) + { + var model = new EditTaxonomyFieldViewModel(); - field.TaxonomyContentItemId = settings.TaxonomyContentItemId; - field.TermContentItemIds = model.TermEntries.Where(x => x.Selected).Select(x => x.ContentItemId).ToArray(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - if (settings.Unique && !string.IsNullOrEmpty(model.UniqueValue)) - { - field.TermContentItemIds = [model.UniqueValue]; - } + var settings = context.PartFieldDefinition.GetSettings(); - if (settings.Required && field.TermContentItemIds.Length == 0) - { - context.Updater.ModelState.AddModelError( - nameof(EditTaxonomyFieldViewModel.TermEntries), - S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + field.TaxonomyContentItemId = settings.TaxonomyContentItemId; + field.TermContentItemIds = model.TermEntries.Where(x => x.Selected).Select(x => x.ContentItemId).ToArray(); - return Edit(field, context); + if (settings.Unique && !string.IsNullOrEmpty(model.UniqueValue)) + { + field.TermContentItemIds = [model.UniqueValue]; } + + if (settings.Required && field.TermContentItemIds.Length == 0) + { + context.Updater.ModelState.AddModelError( + nameof(EditTaxonomyFieldViewModel.TermEntries), + S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDriverHelper.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDriverHelper.cs index b20b9b7509e..3458616a52d 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDriverHelper.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldDriverHelper.cs @@ -7,40 +7,39 @@ using OrchardCore.Taxonomies.Fields; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public static class TaxonomyFieldDriverHelper { - public static class TaxonomyFieldDriverHelper + /// + /// Populates a list of with the hierarchy of terms. + /// The list is ordered so that roots appear right before their child terms. + /// + public static void PopulateTermEntries(List termEntries, TaxonomyField field, IEnumerable contentItems, int level) { - /// - /// Populates a list of with the hierarchy of terms. - /// The list is ordered so that roots appear right before their child terms. - /// - public static void PopulateTermEntries(List termEntries, TaxonomyField field, IEnumerable contentItems, int level) + foreach (var contentItem in contentItems) { - foreach (var contentItem in contentItems) - { - var children = Array.Empty(); + var children = Array.Empty(); - if (((JsonObject)contentItem.Content)["Terms"] is JsonArray termsArray) - { - children = termsArray.ToObject(); - } + if (((JsonObject)contentItem.Content)["Terms"] is JsonArray termsArray) + { + children = termsArray.ToObject(); + } - var termEntry = new TermEntry - { - Term = contentItem, - ContentItemId = contentItem.ContentItemId, - Selected = field.TermContentItemIds.Contains(contentItem.ContentItemId), - Level = level, - IsLeaf = children.Length == 0 - }; + var termEntry = new TermEntry + { + Term = contentItem, + ContentItemId = contentItem.ContentItemId, + Selected = field.TermContentItemIds.Contains(contentItem.ContentItemId), + Level = level, + IsLeaf = children.Length == 0 + }; - termEntries.Add(termEntry); + termEntries.Add(termEntry); - if (children.Length > 0) - { - PopulateTermEntries(termEntries, field, children, level + 1); - } + if (children.Length > 0) + { + PopulateTermEntries(termEntries, field, children, level + 1); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldTagsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldTagsDisplayDriver.cs index 069591235a7..2a2fef9e60f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldTagsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyFieldTagsDisplayDriver.cs @@ -16,103 +16,102 @@ using OrchardCore.Taxonomies.Settings; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public sealed class TaxonomyFieldTagsDisplayDriver : ContentFieldDisplayDriver { - public sealed class TaxonomyFieldTagsDisplayDriver : ContentFieldDisplayDriver - { - private readonly IContentManager _contentManager; + private readonly IContentManager _contentManager; - internal readonly IStringLocalizer S; + internal readonly IStringLocalizer S; - public TaxonomyFieldTagsDisplayDriver( - IContentManager contentManager, - IStringLocalizer stringLocalizer) - { - _contentManager = contentManager; - S = stringLocalizer; - } + public TaxonomyFieldTagsDisplayDriver( + IContentManager contentManager, + IStringLocalizer stringLocalizer) + { + _contentManager = contentManager; + S = stringLocalizer; + } - public override IDisplayResult Display(TaxonomyField field, BuildFieldDisplayContext context) + public override IDisplayResult Display(TaxonomyField field, BuildFieldDisplayContext context) + { + return Initialize(GetDisplayShapeType(context), model => { - return Initialize(GetDisplayShapeType(context), model => - { - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }).Location("Detail", "Content") - .Location("Summary", "Content"); - } + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }).Location("Detail", "Content") + .Location("Summary", "Content"); + } - public override IDisplayResult Edit(TaxonomyField field, BuildFieldEditorContext context) + public override IDisplayResult Edit(TaxonomyField field, BuildFieldEditorContext context) + { + return Initialize(GetEditorShapeType(context), async model => { - return Initialize(GetEditorShapeType(context), async model => - { - var settings = context.PartFieldDefinition.GetSettings(); - model.Taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); + var settings = context.PartFieldDefinition.GetSettings(); + model.Taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); - if (model.Taxonomy != null) + if (model.Taxonomy != null) + { + var termEntries = new List(); + TaxonomyFieldDriverHelper.PopulateTermEntries(termEntries, field, model.Taxonomy.As().Terms, 0); + var tagTermEntries = termEntries.Select(te => new TagTermEntry { - var termEntries = new List(); - TaxonomyFieldDriverHelper.PopulateTermEntries(termEntries, field, model.Taxonomy.As().Terms, 0); - var tagTermEntries = termEntries.Select(te => new TagTermEntry - { - ContentItemId = te.ContentItemId, - Selected = te.Selected, - DisplayText = te.Term.DisplayText, - IsLeaf = te.IsLeaf - }); - - model.TagTermEntries = JNode.FromObject(tagTermEntries, JOptions.CamelCase).ToJsonString(JOptions.Default); - } - - model.Field = field; - model.Part = context.ContentPart; - model.PartFieldDefinition = context.PartFieldDefinition; - }); - } + ContentItemId = te.ContentItemId, + Selected = te.Selected, + DisplayText = te.Term.DisplayText, + IsLeaf = te.IsLeaf + }); - public override async Task UpdateAsync(TaxonomyField field, UpdateFieldEditorContext context) - { - var model = new EditTagTaxonomyFieldViewModel(); + model.TagTermEntries = JNode.FromObject(tagTermEntries, JOptions.CamelCase).ToJsonString(JOptions.Default); + } - await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.TermContentItemIds); + model.Field = field; + model.Part = context.ContentPart; + model.PartFieldDefinition = context.PartFieldDefinition; + }); + } - var settings = context.PartFieldDefinition.GetSettings(); + public override async Task UpdateAsync(TaxonomyField field, UpdateFieldEditorContext context) + { + var model = new EditTagTaxonomyFieldViewModel(); - field.TaxonomyContentItemId = settings.TaxonomyContentItemId; + await context.Updater.TryUpdateModelAsync(model, Prefix, f => f.TermContentItemIds); - field.TermContentItemIds = model.TermContentItemIds == null - ? [] : model.TermContentItemIds.Split(',', StringSplitOptions.RemoveEmptyEntries); + var settings = context.PartFieldDefinition.GetSettings(); - if (settings.Required && field.TermContentItemIds.Length == 0) - { - context.Updater.ModelState.AddModelError( - nameof(EditTagTaxonomyFieldViewModel.TermContentItemIds), - S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); - } + field.TaxonomyContentItemId = settings.TaxonomyContentItemId; - // Update display text for tags. - var taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); + field.TermContentItemIds = model.TermContentItemIds == null + ? [] : model.TermContentItemIds.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (taxonomy == null) - { - return null; - } + if (settings.Required && field.TermContentItemIds.Length == 0) + { + context.Updater.ModelState.AddModelError( + nameof(EditTagTaxonomyFieldViewModel.TermContentItemIds), + S["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]); + } - var terms = new List(); + // Update display text for tags. + var taxonomy = await _contentManager.GetAsync(settings.TaxonomyContentItemId, VersionOptions.Latest); - foreach (var termContentItemId in field.TermContentItemIds) - { - var term = TaxonomyOrchardHelperExtensions.FindTerm( - (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], - termContentItemId); + if (taxonomy == null) + { + return null; + } - terms.Add(term); - } + var terms = new List(); - field.SetTagNames(terms.Select(t => t.DisplayText).ToArray()); + foreach (var termContentItemId in field.TermContentItemIds) + { + var term = TaxonomyOrchardHelperExtensions.FindTerm( + (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], + termContentItemId); - return Edit(field, context); + terms.Add(term); } + + field.SetTagNames(terms.Select(t => t.DisplayText).ToArray()); + + return Edit(field, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyPartDisplayDriver.cs index 71a3da03a20..f744f32c857 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TaxonomyPartDisplayDriver.cs @@ -13,119 +13,118 @@ using OrchardCore.Taxonomies.Models; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public sealed class TaxonomyPartDisplayDriver : ContentPartDisplayDriver { - public sealed class TaxonomyPartDisplayDriver : ContentPartDisplayDriver + internal readonly IStringLocalizer S; + + public TaxonomyPartDisplayDriver(IStringLocalizer stringLocalizer) + { + S = stringLocalizer; + } + + public override IDisplayResult Display(TaxonomyPart part, BuildPartDisplayContext context) { - internal readonly IStringLocalizer S; + var hasItems = part.Terms.Count > 0; - public TaxonomyPartDisplayDriver(IStringLocalizer stringLocalizer) + return Initialize(hasItems ? "TaxonomyPart" : "TaxonomyPart_Empty", m => { - S = stringLocalizer; - } + m.ContentItem = part.ContentItem; + m.TaxonomyPart = part; + }).Location("Detail", "Content"); + } - public override IDisplayResult Display(TaxonomyPart part, BuildPartDisplayContext context) + public override IDisplayResult Edit(TaxonomyPart part, BuildPartEditorContext context) + { + return Initialize("TaxonomyPart_Edit", model => { - var hasItems = part.Terms.Count > 0; + model.TermContentType = part.TermContentType; + model.TaxonomyPart = part; + }); + } - return Initialize(hasItems ? "TaxonomyPart" : "TaxonomyPart_Empty", m => - { - m.ContentItem = part.ContentItem; - m.TaxonomyPart = part; - }).Location("Detail", "Content"); - } + public override async Task UpdateAsync(TaxonomyPart part, UpdatePartEditorContext context) + { + var model = new TaxonomyPartEditViewModel(); + + await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Hierarchy, t => t.TermContentType); - public override IDisplayResult Edit(TaxonomyPart part, BuildPartEditorContext context) + if (string.IsNullOrWhiteSpace(model.TermContentType)) { - return Initialize("TaxonomyPart_Edit", model => - { - model.TermContentType = part.TermContentType; - model.TaxonomyPart = part; - }); + context.Updater.ModelState.AddModelError(Prefix, nameof(model.TermContentType), S["The Term Content Type field is required."]); } - public override async Task UpdateAsync(TaxonomyPart part, UpdatePartEditorContext context) + if (!string.IsNullOrWhiteSpace(model.Hierarchy)) { - var model = new TaxonomyPartEditViewModel(); + var originalTaxonomyItems = part.ContentItem.As(); + + var newHierarchy = JsonNode.Parse(model.Hierarchy).AsArray(); - await context.Updater.TryUpdateModelAsync(model, Prefix, t => t.Hierarchy, t => t.TermContentType); + var taxonomyItems = new JsonArray(); - if (string.IsNullOrWhiteSpace(model.TermContentType)) + foreach (var item in newHierarchy) { - context.Updater.ModelState.AddModelError(Prefix, nameof(model.TermContentType), S["The Term Content Type field is required."]); + taxonomyItems.Add(ProcessItem(originalTaxonomyItems, item as JsonObject)); } - if (!string.IsNullOrWhiteSpace(model.Hierarchy)) - { - var originalTaxonomyItems = part.ContentItem.As(); + part.Terms = taxonomyItems.ToObject>(); + } - var newHierarchy = JsonNode.Parse(model.Hierarchy).AsArray(); + part.TermContentType = model.TermContentType; - var taxonomyItems = new JsonArray(); + return Edit(part, context); + } - foreach (var item in newHierarchy) - { - taxonomyItems.Add(ProcessItem(originalTaxonomyItems, item as JsonObject)); - } + /// + /// Clone the content items at the specific index. + /// + private static JsonObject GetTaxonomyItemAt(List taxonomyItems, int[] indexes) + { + ContentItem taxonomyItem = null; - part.Terms = taxonomyItems.ToObject>(); + // Seek the term represented by the list of indexes + foreach (var index in indexes) + { + if (taxonomyItems == null || taxonomyItems.Count < index) + { + // Trying to access an unknown index + return null; } - part.TermContentType = model.TermContentType; + taxonomyItem = taxonomyItems[index]; - return Edit(part, context); + var terms = (JsonArray)taxonomyItem.Content["Terms"]; + taxonomyItems = terms?.ToObject>(); } - /// - /// Clone the content items at the specific index. - /// - private static JsonObject GetTaxonomyItemAt(List taxonomyItems, int[] indexes) - { - ContentItem taxonomyItem = null; - - // Seek the term represented by the list of indexes - foreach (var index in indexes) - { - if (taxonomyItems == null || taxonomyItems.Count < index) - { - // Trying to access an unknown index - return null; - } + var newObj = JObject.FromObject(taxonomyItem); - taxonomyItem = taxonomyItems[index]; - - var terms = (JsonArray)taxonomyItem.Content["Terms"]; - taxonomyItems = terms?.ToObject>(); - } + if (newObj["Terms"] != null) + { + newObj["Terms"] = new JsonArray(); + } - var newObj = JObject.FromObject(taxonomyItem); + return newObj; + } - if (newObj["Terms"] != null) - { - newObj["Terms"] = new JsonArray(); - } + private static JsonObject ProcessItem(TaxonomyPart originalItems, JsonObject item) + { + var contentItem = GetTaxonomyItemAt(originalItems.Terms, item["index"].ToString().Split('-').Select(x => Convert.ToInt32(x)).ToArray()); - return newObj; - } + var children = item["children"] as JsonArray; - private static JsonObject ProcessItem(TaxonomyPart originalItems, JsonObject item) + if (children is not null) { - var contentItem = GetTaxonomyItemAt(originalItems.Terms, item["index"].ToString().Split('-').Select(x => Convert.ToInt32(x)).ToArray()); + var taxonomyItems = new JsonArray(); - var children = item["children"] as JsonArray; - - if (children is not null) + for (var i = 0; i < children.Count; i++) { - var taxonomyItems = new JsonArray(); - - for (var i = 0; i < children.Count; i++) - { - taxonomyItems.Add(ProcessItem(originalItems, children[i] as JsonObject)); - contentItem["Terms"] = taxonomyItems; - } + taxonomyItems.Add(ProcessItem(originalItems, children[i] as JsonObject)); + contentItem["Terms"] = taxonomyItems; } - - return contentItem; } + + return contentItem; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TermPartContentDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TermPartContentDriver.cs index 410ded748a5..b7c7a3ed54e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TermPartContentDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Drivers/TermPartContentDriver.cs @@ -16,153 +16,152 @@ using OrchardCore.Taxonomies.ViewModels; using YesSql; -namespace OrchardCore.Taxonomies.Drivers +namespace OrchardCore.Taxonomies.Drivers; + +public sealed class TermPartContentDriver : ContentDisplayDriver { - public sealed class TermPartContentDriver : ContentDisplayDriver + private readonly ISession _session; + private readonly PagerOptions _pagerOptions; + private readonly IContentManager _contentManager; + + public TermPartContentDriver( + ISession session, + IOptions pagerOptions, + IContentManager contentManager) { - private readonly ISession _session; - private readonly PagerOptions _pagerOptions; - private readonly IContentManager _contentManager; - - public TermPartContentDriver( - ISession session, - IOptions pagerOptions, - IContentManager contentManager) - { - _session = session; - _pagerOptions = pagerOptions.Value; - _contentManager = contentManager; - } + _session = session; + _pagerOptions = pagerOptions.Value; + _contentManager = contentManager; + } - public override IDisplayResult Display(ContentItem model, BuildDisplayContext context) + public override IDisplayResult Display(ContentItem model, BuildDisplayContext context) + { + var part = model.As(); + if (part != null) { - var part = model.As(); - if (part != null) + return Initialize("TermPart", async m => { - return Initialize("TermPart", async m => - { - var pager = await GetPagerAsync(context.Updater, _pagerOptions.GetPageSize()); - m.TaxonomyContentItemId = part.TaxonomyContentItemId; - m.ContentItem = part.ContentItem; - m.ContentItems = (await QueryTermItemsAsync(part, pager)).ToArray(); - m.Pager = await context.New.PagerSlim(pager); - }).Location("Detail", "Content:5"); - } - - return null; + var pager = await GetPagerAsync(context.Updater, _pagerOptions.GetPageSize()); + m.TaxonomyContentItemId = part.TaxonomyContentItemId; + m.ContentItem = part.ContentItem; + m.ContentItems = (await QueryTermItemsAsync(part, pager)).ToArray(); + m.Pager = await context.New.PagerSlim(pager); + }).Location("Detail", "Content:5"); } - private async Task> QueryTermItemsAsync(TermPart termPart, PagerSlim pager) - { - if (pager.Before != null) - { - var beforeValue = new DateTime(long.Parse(pager.Before)); - var query = _session.Query() - .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) - .With(CreateContentIndexFilter(beforeValue, null)) - .OrderBy(x => x.CreatedUtc) - .Take(pager.PageSize + 1); + return null; + } - var containedItems = await query.ListAsync(); + private async Task> QueryTermItemsAsync(TermPart termPart, PagerSlim pager) + { + if (pager.Before != null) + { + var beforeValue = new DateTime(long.Parse(pager.Before)); + var query = _session.Query() + .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) + .With(CreateContentIndexFilter(beforeValue, null)) + .OrderBy(x => x.CreatedUtc) + .Take(pager.PageSize + 1); - if (!containedItems.Any()) - { - return containedItems; - } + var containedItems = await query.ListAsync(); - containedItems = containedItems.Reverse(); + if (!containedItems.Any()) + { + return containedItems; + } - // There is always an After as we clicked on Before. - pager.Before = null; - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); + containedItems = containedItems.Reverse(); - if (containedItems.Count() == pager.PageSize + 1) - { - containedItems = containedItems.Skip(1); - pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); - } + // There is always an After as we clicked on Before. + pager.Before = null; + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - return await _contentManager.LoadAsync(containedItems); - } - else if (pager.After != null) + if (containedItems.Count() == pager.PageSize + 1) { - var afterValue = new DateTime(long.Parse(pager.After)); - var query = _session.Query() - .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) - .With(CreateContentIndexFilter(null, afterValue)) - .OrderByDescending(x => x.CreatedUtc) - .Take(pager.PageSize + 1); + containedItems = containedItems.Skip(1); + pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); + } - var containedItems = await query.ListAsync(); + return await _contentManager.LoadAsync(containedItems); + } + else if (pager.After != null) + { + var afterValue = new DateTime(long.Parse(pager.After)); + var query = _session.Query() + .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) + .With(CreateContentIndexFilter(null, afterValue)) + .OrderByDescending(x => x.CreatedUtc) + .Take(pager.PageSize + 1); - if (!containedItems.Any()) - { - return containedItems; - } + var containedItems = await query.ListAsync(); - // There is always a Before page as we clicked on After. - pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); - pager.After = null; + if (!containedItems.Any()) + { + return containedItems; + } - if (containedItems.Count() == pager.PageSize + 1) - { - containedItems = containedItems.Take(pager.PageSize); - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - } + // There is always a Before page as we clicked on After. + pager.Before = containedItems.First().CreatedUtc.Value.Ticks.ToString(); + pager.After = null; - return await _contentManager.LoadAsync(containedItems); - } - else + if (containedItems.Count() == pager.PageSize + 1) { - var query = _session.Query() - .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) - .With(CreateContentIndexFilter(null, null)) - .OrderByDescending(x => x.CreatedUtc) - .Take(pager.PageSize + 1); + containedItems = containedItems.Take(pager.PageSize); + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); + } - var containedItems = await query.ListAsync(); + return await _contentManager.LoadAsync(containedItems); + } + else + { + var query = _session.Query() + .With(x => x.TermContentItemId == termPart.ContentItem.ContentItemId) + .With(CreateContentIndexFilter(null, null)) + .OrderByDescending(x => x.CreatedUtc) + .Take(pager.PageSize + 1); - if (!containedItems.Any()) - { - return containedItems; - } + var containedItems = await query.ListAsync(); - pager.Before = null; - pager.After = null; + if (!containedItems.Any()) + { + return containedItems; + } - if (containedItems.Count() == pager.PageSize + 1) - { - containedItems = containedItems.Take(pager.PageSize); - pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); - } + pager.Before = null; + pager.After = null; - return await _contentManager.LoadAsync(containedItems); + if (containedItems.Count() == pager.PageSize + 1) + { + containedItems = containedItems.Take(pager.PageSize); + pager.After = containedItems.Last().CreatedUtc.Value.Ticks.ToString(); } + + return await _contentManager.LoadAsync(containedItems); } + } - private static async Task GetPagerAsync(IUpdateModel updater, int pageSize) - { - var pagerParameters = new PagerSlimParameters(); - await updater.TryUpdateModelAsync(pagerParameters); + private static async Task GetPagerAsync(IUpdateModel updater, int pageSize) + { + var pagerParameters = new PagerSlimParameters(); + await updater.TryUpdateModelAsync(pagerParameters); - var pager = new PagerSlim(pagerParameters, pageSize); + var pager = new PagerSlim(pagerParameters, pageSize); - return pager; - } + return pager; + } - private static Expression> CreateContentIndexFilter(DateTime? before, DateTime? after) + private static Expression> CreateContentIndexFilter(DateTime? before, DateTime? after) + { + if (before != null) { - if (before != null) - { - return x => x.Published && x.CreatedUtc > before; - } - - if (after != null) - { - return x => x.Published && x.CreatedUtc < after; - } + return x => x.Published && x.CreatedUtc > before; + } - return x => x.Published; + if (after != null) + { + return x => x.Published && x.CreatedUtc < after; } + + return x => x.Published; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TagNamesExtensions.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TagNamesExtensions.cs index 2c8dc075fd3..8bef07eb1df 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TagNamesExtensions.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TagNamesExtensions.cs @@ -1,28 +1,27 @@ using System.Text.Json.Nodes; -namespace OrchardCore.Taxonomies.Fields +namespace OrchardCore.Taxonomies.Fields; + +public static class TagNamesExtensions { - public static class TagNamesExtensions + /// + /// Tag names are a less well known property of a taxonomy field + /// managed by the tags editor and tags display mode. + /// + public static string[] GetTagNames(this TaxonomyField taxonomyField) { - /// - /// Tag names are a less well known property of a taxonomy field - /// managed by the tags editor and tags display mode. - /// - public static string[] GetTagNames(this TaxonomyField taxonomyField) - { - var tagNames = (JsonArray)taxonomyField.Content["TagNames"]; + var tagNames = (JsonArray)taxonomyField.Content["TagNames"]; - return tagNames != null ? tagNames.ToObject() : []; - } + return tagNames != null ? tagNames.ToObject() : []; + } - /// - /// Tags names are a less well known property of a taxonomy field - /// managed by the tags editor and tags display mode. - /// They are updated only when saving the content item. - /// - public static void SetTagNames(this TaxonomyField taxonomyField, string[] tagNames) - { - taxonomyField.Content["TagNames"] = JArray.FromObject(tagNames); - } + /// + /// Tags names are a less well known property of a taxonomy field + /// managed by the tags editor and tags display mode. + /// They are updated only when saving the content item. + /// + public static void SetTagNames(this TaxonomyField taxonomyField, string[] tagNames) + { + taxonomyField.Content["TagNames"] = JArray.FromObject(tagNames); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TaxonomyField.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TaxonomyField.cs index 9016b584adb..b2b046a7dd6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TaxonomyField.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Fields/TaxonomyField.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Taxonomies.Fields +namespace OrchardCore.Taxonomies.Fields; + +public class TaxonomyField : ContentField { - public class TaxonomyField : ContentField - { - public string TaxonomyContentItemId { get; set; } - public string[] TermContentItemIds { get; set; } = []; - } + public string TaxonomyContentItemId { get; set; } + public string[] TermContentItemIds { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs index ad4942e525d..4a6767c0058 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyFieldQueryObjectType.cs @@ -7,66 +7,65 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.GraphQL +namespace OrchardCore.Taxonomies.GraphQL; + +public class TaxonomyFieldQueryObjectType : ObjectGraphType { - public class TaxonomyFieldQueryObjectType : ObjectGraphType + public TaxonomyFieldQueryObjectType() { - public TaxonomyFieldQueryObjectType() - { - Name = nameof(TaxonomyField); + Name = nameof(TaxonomyField); - Field, IEnumerable>("termContentItemIds") - .Description("term content item ids") - .PagingArguments() - .Resolve(x => - { - return x.Page(x.Source.TermContentItemIds); - }); + Field, IEnumerable>("termContentItemIds") + .Description("term content item ids") + .PagingArguments() + .Resolve(x => + { + return x.Page(x.Source.TermContentItemIds); + }); - Field("taxonomyContentItemId") - .Description("taxonomy content item id") - .Resolve(x => - { - return x.Source.TaxonomyContentItemId; - }); + Field("taxonomyContentItemId") + .Description("taxonomy content item id") + .Resolve(x => + { + return x.Source.TaxonomyContentItemId; + }); - Field, List>("termContentItems") - .Description("the term content items") - .PagingArguments() - .ResolveLockedAsync(async x => - { - var ids = x.Page(x.Source.TermContentItemIds); - var contentManager = x.RequestServices.GetService(); + Field, List>("termContentItems") + .Description("the term content items") + .PagingArguments() + .ResolveLockedAsync(async x => + { + var ids = x.Page(x.Source.TermContentItemIds); + var contentManager = x.RequestServices.GetService(); - var taxonomy = await contentManager.GetAsync(x.Source.TaxonomyContentItemId); + var taxonomy = await contentManager.GetAsync(x.Source.TaxonomyContentItemId); - if (taxonomy == null) - { - return null; - } + if (taxonomy == null) + { + return null; + } - var terms = new List(); + var terms = new List(); - foreach (var termContentItemId in ids) - { - var term = TaxonomyOrchardHelperExtensions.FindTerm( - (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], - termContentItemId); + foreach (var termContentItemId in ids) + { + var term = TaxonomyOrchardHelperExtensions.FindTerm( + (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], + termContentItemId); - terms.Add(term); - } + terms.Add(term); + } - return terms; - }); + return terms; + }); - Field("taxonomyContentItem") - .Description("the taxonomy content item") - .ResolveLockedAsync(async context => - { - var contentManager = context.RequestServices.GetService(); + Field("taxonomyContentItem") + .Description("the taxonomy content item") + .ResolveLockedAsync(async context => + { + var contentManager = context.RequestServices.GetService(); - return await contentManager.GetAsync(context.Source.TaxonomyContentItemId); - }); - } + return await contentManager.GetAsync(context.Source.TaxonomyContentItemId); + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs index f1fe006fa68..b2f80b6cd10 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/GraphQL/TaxonomyPartQueryObjectType.cs @@ -5,20 +5,19 @@ using OrchardCore.ContentManagement.GraphQL.Queries.Types; using OrchardCore.Taxonomies.Models; -namespace OrchardCore.Taxonomies.GraphQL +namespace OrchardCore.Taxonomies.GraphQL; + +public class TaxonomyPartQueryObjectType : ObjectGraphType { - public class TaxonomyPartQueryObjectType : ObjectGraphType + public TaxonomyPartQueryObjectType() { - public TaxonomyPartQueryObjectType() - { - Name = "TaxonomyPart"; + Name = "TaxonomyPart"; - Field(x => x.TermContentType); + Field(x => x.TermContentType); - Field, IEnumerable>("contentItems") - .Description("the content items") - .PagingArguments() - .Resolve(x => x.Page(x.Source.Terms)); - } + Field, IEnumerable>("contentItems") + .Description("the content items") + .PagingArguments() + .Resolve(x => x.Page(x.Source.Terms)); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TaxonomyPartHandler.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TaxonomyPartHandler.cs index 58fd760402c..582cfc2f094 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TaxonomyPartHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TaxonomyPartHandler.cs @@ -4,21 +4,20 @@ using OrchardCore.ContentManagement.Routing; using OrchardCore.Taxonomies.Models; -namespace OrchardCore.Taxonomies.Handlers +namespace OrchardCore.Taxonomies.Handlers; + +public class TaxonomyPartHandler : ContentPartHandler { - public class TaxonomyPartHandler : ContentPartHandler + public override Task GetContentItemAspectAsync(ContentItemAspectContext context, TaxonomyPart part) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context, TaxonomyPart part) + return context.ForAsync(aspect => { - return context.ForAsync(aspect => + aspect.Accessors.Add((jsonObject) => { - aspect.Accessors.Add((jsonObject) => - { - return jsonObject["TaxonomyPart"]["Terms"] as JsonArray; - }); - - return Task.CompletedTask; + return jsonObject["TaxonomyPart"]["Terms"] as JsonArray; }); - } + + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TermPartContentHandler.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TermPartContentHandler.cs index 2fde41a6d46..de0c8b2c596 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TermPartContentHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Handlers/TermPartContentHandler.cs @@ -3,25 +3,24 @@ using OrchardCore.ContentManagement.Handlers; using OrchardCore.ContentManagement.Routing; -namespace OrchardCore.Taxonomies.Handlers +namespace OrchardCore.Taxonomies.Handlers; + +public class TermPartContentHandler : ContentHandlerBase { - public class TermPartContentHandler : ContentHandlerBase + public override Task GetContentItemAspectAsync(ContentItemAspectContext context) { - public override Task GetContentItemAspectAsync(ContentItemAspectContext context) + return context.ForAsync(aspect => { - return context.ForAsync(aspect => + // Check this content item contains Terms. + if (((JsonNode)context.ContentItem.Content)["Terms"] is JsonArray) { - // Check this content item contains Terms. - if (((JsonNode)context.ContentItem.Content)["Terms"] is JsonArray) + aspect.Accessors.Add((jsonObject) => { - aspect.Accessors.Add((jsonObject) => - { - return jsonObject["Terms"] as JsonArray; - }); - } + return jsonObject["Terms"] as JsonArray; + }); + } - return Task.CompletedTask; - }); - } + return Task.CompletedTask; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyFieldIndexHandler.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyFieldIndexHandler.cs index cb0882fbded..0816ce762a2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyFieldIndexHandler.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyFieldIndexHandler.cs @@ -8,49 +8,48 @@ using OrchardCore.Indexing; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.Indexing +namespace OrchardCore.Taxonomies.Indexing; + +public class TaxonomyFieldIndexHandler : ContentFieldIndexHandler { - public class TaxonomyFieldIndexHandler : ContentFieldIndexHandler - { - private readonly IServiceProvider _serviceProvider; + private readonly IServiceProvider _serviceProvider; - public TaxonomyFieldIndexHandler(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } + public TaxonomyFieldIndexHandler(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } - public override async Task BuildIndexAsync(TaxonomyField field, BuildFieldIndexContext context) - { - // TODO: Also add the parents of each term, probably as a separate field + public override async Task BuildIndexAsync(TaxonomyField field, BuildFieldIndexContext context) + { + // TODO: Also add the parents of each term, probably as a separate field - var options = context.Settings.ToOptions(); - options |= DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; + var options = context.Settings.ToOptions(); + options |= DocumentIndexOptions.Keyword | DocumentIndexOptions.Store; - // Directly selected term ids are added to the default field name - foreach (var contentItemId in field.TermContentItemIds) + // Directly selected term ids are added to the default field name + foreach (var contentItemId in field.TermContentItemIds) + { + foreach (var key in context.Keys) { - foreach (var key in context.Keys) - { - context.DocumentIndex.Set(key + IndexingConstants.IdsKey, contentItemId, options); - } + context.DocumentIndex.Set(key + IndexingConstants.IdsKey, contentItemId, options); } + } - // Inherited term ids are added to a distinct field, prefixed with "Inherited" - var contentManager = _serviceProvider.GetRequiredService(); - var taxonomy = await contentManager.GetAsync(field.TaxonomyContentItemId); + // Inherited term ids are added to a distinct field, prefixed with "Inherited" + var contentManager = _serviceProvider.GetRequiredService(); + var taxonomy = await contentManager.GetAsync(field.TaxonomyContentItemId); - var inheritedContentItems = new List(); - foreach (var contentItemId in field.TermContentItemIds) - { - TaxonomyOrchardHelperExtensions.FindTermHierarchy((JsonArray)taxonomy.Content.TaxonomyPart.Terms, contentItemId, inheritedContentItems); - } + var inheritedContentItems = new List(); + foreach (var contentItemId in field.TermContentItemIds) + { + TaxonomyOrchardHelperExtensions.FindTermHierarchy((JsonArray)taxonomy.Content.TaxonomyPart.Terms, contentItemId, inheritedContentItems); + } - foreach (var key in context.Keys) + foreach (var key in context.Keys) + { + foreach (var contentItem in inheritedContentItems) { - foreach (var contentItem in inheritedContentItems) - { - context.DocumentIndex.Set(key + IndexingConstants.InheritedKey, contentItem.ContentItemId, options); - } + context.DocumentIndex.Set(key + IndexingConstants.InheritedKey, contentItem.ContentItemId, options); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyIndex.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyIndex.cs index 2a48c14b072..d5572077ad8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyIndex.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Indexing/TaxonomyIndex.cs @@ -9,110 +9,109 @@ using OrchardCore.Taxonomies.Fields; using YesSql.Indexes; -namespace OrchardCore.Taxonomies.Indexing +namespace OrchardCore.Taxonomies.Indexing; + +public class TaxonomyIndex : MapIndex +{ + public string TaxonomyContentItemId { get; set; } + public string ContentItemId { get; set; } + public string ContentType { get; set; } + public string ContentPart { get; set; } + public string ContentField { get; set; } + public string TermContentItemId { get; set; } + public bool Published { get; set; } + public bool Latest { get; set; } +} + +public class TaxonomyIndexProvider : IndexProvider, IScopedIndexProvider { - public class TaxonomyIndex : MapIndex + private readonly IServiceProvider _serviceProvider; + private readonly HashSet _ignoredTypes = []; + private IContentDefinitionManager _contentDefinitionManager; + + public TaxonomyIndexProvider( + IServiceProvider serviceProvider) { - public string TaxonomyContentItemId { get; set; } - public string ContentItemId { get; set; } - public string ContentType { get; set; } - public string ContentPart { get; set; } - public string ContentField { get; set; } - public string TermContentItemId { get; set; } - public bool Published { get; set; } - public bool Latest { get; set; } + _serviceProvider = serviceProvider; } - public class TaxonomyIndexProvider : IndexProvider, IScopedIndexProvider + public override void Describe(DescribeContext context) { - private readonly IServiceProvider _serviceProvider; - private readonly HashSet _ignoredTypes = []; - private IContentDefinitionManager _contentDefinitionManager; - - public TaxonomyIndexProvider( - IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public override void Describe(DescribeContext context) - { - context.For() - .Map(async contentItem => + context.For() + .Map(async contentItem => + { + // Remove index records of soft deleted items. + if (!contentItem.Published && !contentItem.Latest) { - // Remove index records of soft deleted items. - if (!contentItem.Published && !contentItem.Latest) - { - return null; - } + return null; + } - // Can we safely ignore this content item? - if (_ignoredTypes.Contains(contentItem.ContentType)) - { - return null; - } + // Can we safely ignore this content item? + if (_ignoredTypes.Contains(contentItem.ContentType)) + { + return null; + } - // Lazy initialization because of ISession cyclic dependency - _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); + // Lazy initialization because of ISession cyclic dependency + _contentDefinitionManager ??= _serviceProvider.GetRequiredService(); - // Search for Taxonomy fields - var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); + // Search for Taxonomy fields + var contentTypeDefinition = await _contentDefinitionManager.GetTypeDefinitionAsync(contentItem.ContentType); - // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. - if (contentTypeDefinition == null) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This can occur when content items become orphaned, particularly layer widgets when a layer is removed, before its widgets have been unpublished. + if (contentTypeDefinition == null) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var fieldDefinitions = contentTypeDefinition - .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TaxonomyField))) - .ToArray(); + var fieldDefinitions = contentTypeDefinition + .Parts.SelectMany(x => x.PartDefinition.Fields.Where(f => f.FieldDefinition.Name == nameof(TaxonomyField))) + .ToArray(); - // This type doesn't have any TaxonomyField, ignore it - if (fieldDefinitions.Length == 0) - { - _ignoredTypes.Add(contentItem.ContentType); - return null; - } + // This type doesn't have any TaxonomyField, ignore it + if (fieldDefinitions.Length == 0) + { + _ignoredTypes.Add(contentItem.ContentType); + return null; + } - var results = new List(); + var results = new List(); - // Get all field values - foreach (var fieldDefinition in fieldDefinitions) + // Get all field values + foreach (var fieldDefinition in fieldDefinitions) + { + var jPart = contentItem.Content[fieldDefinition.PartDefinition.Name]; + if (jPart is null) { - var jPart = contentItem.Content[fieldDefinition.PartDefinition.Name]; - if (jPart is null) - { - continue; - } + continue; + } - var jField = jPart[fieldDefinition.Name]; - if (jField is null) - { - continue; - } + var jField = jPart[fieldDefinition.Name]; + if (jField is null) + { + continue; + } - var field = ((JsonObject)jField).ToObject(); + var field = ((JsonObject)jField).ToObject(); - foreach (var termContentItemId in field.TermContentItemIds) + foreach (var termContentItemId in field.TermContentItemIds) + { + results.Add(new TaxonomyIndex { - results.Add(new TaxonomyIndex - { - TaxonomyContentItemId = field.TaxonomyContentItemId, - ContentItemId = contentItem.ContentItemId, - ContentType = contentItem.ContentType, - ContentPart = fieldDefinition.PartDefinition.Name, - ContentField = fieldDefinition.Name, - TermContentItemId = termContentItemId, - Published = contentItem.Published, - Latest = contentItem.Latest - }); - } + TaxonomyContentItemId = field.TaxonomyContentItemId, + ContentItemId = contentItem.ContentItemId, + ContentType = contentItem.ContentType, + ContentPart = fieldDefinition.PartDefinition.Name, + ContentField = fieldDefinition.Name, + TermContentItemId = termContentItemId, + Published = contentItem.Published, + Latest = contentItem.Latest + }); } + } - return results; - }); - } + return results; + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/InheritedTermsFilter.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/InheritedTermsFilter.cs index 7bb674db146..3b10d93bd1c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/InheritedTermsFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/InheritedTermsFilter.cs @@ -6,39 +6,38 @@ using OrchardCore.ContentManagement; using OrchardCore.Liquid; -namespace OrchardCore.Taxonomies.Liquid +namespace OrchardCore.Taxonomies.Liquid; + +public class InheritedTermsFilter : ILiquidFilter { - public class InheritedTermsFilter : ILiquidFilter + private readonly IContentManager _contentManager; + + public InheritedTermsFilter(IContentManager contentManager) + { + _contentManager = contentManager; + } + + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) { - private readonly IContentManager _contentManager; + var termContentItemId = input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem term + ? term.ContentItemId + : input.ToStringValue(); - public InheritedTermsFilter(IContentManager contentManager) + var firstArg = arguments.At(0); + if (firstArg.Type != FluidValues.Object || input.ToObjectValue() is not ContentItem taxonomy) { - _contentManager = contentManager; + taxonomy = await _contentManager.GetAsync(firstArg.ToStringValue()); } - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + if (taxonomy == null) { - var termContentItemId = input.Type == FluidValues.Object && input.ToObjectValue() is ContentItem term - ? term.ContentItemId - : input.ToStringValue(); - - var firstArg = arguments.At(0); - if (firstArg.Type != FluidValues.Object || input.ToObjectValue() is not ContentItem taxonomy) - { - taxonomy = await _contentManager.GetAsync(firstArg.ToStringValue()); - } - - if (taxonomy == null) - { - return null; - } + return null; + } - var terms = new List(); + var terms = new List(); - TaxonomyOrchardHelperExtensions.FindTermHierarchy((JsonArray)taxonomy.Content.TaxonomyPart.Terms, termContentItemId, terms); + TaxonomyOrchardHelperExtensions.FindTermHierarchy((JsonArray)taxonomy.Content.TaxonomyPart.Terms, termContentItemId, terms); - return FluidValue.Create(terms, ctx.Options); - } + return FluidValue.Create(terms, ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/TaxonomyTermsFilter.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/TaxonomyTermsFilter.cs index ad2f392701a..3c7446a311e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/TaxonomyTermsFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Liquid/TaxonomyTermsFilter.cs @@ -8,67 +8,66 @@ using OrchardCore.Liquid; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.Liquid +namespace OrchardCore.Taxonomies.Liquid; + +public class TaxonomyTermsFilter : ILiquidFilter { - public class TaxonomyTermsFilter : ILiquidFilter + private readonly IContentManager _contentManager; + + public TaxonomyTermsFilter(IContentManager contentManager) { - private readonly IContentManager _contentManager; + _contentManager = contentManager; + } - public TaxonomyTermsFilter(IContentManager contentManager) + public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + { + string taxonomyContentItemId = null; + string[] termContentItemIds = null; + + if (input.Type == FluidValues.Object && input.ToObjectValue() is TaxonomyField field) { - _contentManager = contentManager; + taxonomyContentItemId = field.TaxonomyContentItemId; + termContentItemIds = field.TermContentItemIds; } - - public async ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext ctx) + else if (input.Type == FluidValues.Object + && input.ToObjectValue() is JsonObject jobj + && jobj.ContainsKey(nameof(TaxonomyField.TermContentItemIds)) + && jobj.ContainsKey(nameof(TaxonomyField.TaxonomyContentItemId))) + { + taxonomyContentItemId = jobj["TaxonomyContentItemId"].Value(); + termContentItemIds = jobj["TermContentItemIds"].Values().ToArray(); + } + else if (input.Type == FluidValues.Array) + { + taxonomyContentItemId = arguments.At(0).ToStringValue(); + termContentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue()).ToArray(); + } + else { - string taxonomyContentItemId = null; - string[] termContentItemIds = null; + return NilValue.Instance; + } - if (input.Type == FluidValues.Object && input.ToObjectValue() is TaxonomyField field) - { - taxonomyContentItemId = field.TaxonomyContentItemId; - termContentItemIds = field.TermContentItemIds; - } - else if (input.Type == FluidValues.Object - && input.ToObjectValue() is JsonObject jobj - && jobj.ContainsKey(nameof(TaxonomyField.TermContentItemIds)) - && jobj.ContainsKey(nameof(TaxonomyField.TaxonomyContentItemId))) - { - taxonomyContentItemId = jobj["TaxonomyContentItemId"].Value(); - termContentItemIds = jobj["TermContentItemIds"].Values().ToArray(); - } - else if (input.Type == FluidValues.Array) - { - taxonomyContentItemId = arguments.At(0).ToStringValue(); - termContentItemIds = input.Enumerate(ctx).Select(x => x.ToStringValue()).ToArray(); - } - else - { - return NilValue.Instance; - } + var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId); - var taxonomy = await _contentManager.GetAsync(taxonomyContentItemId); + if (taxonomy == null) + { + return null; + } - if (taxonomy == null) - { - return null; - } + var terms = new List(); - var terms = new List(); + foreach (var termContentItemId in termContentItemIds) + { + var term = TaxonomyOrchardHelperExtensions.FindTerm( + (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], + termContentItemId); - foreach (var termContentItemId in termContentItemIds) + if (term is not null) { - var term = TaxonomyOrchardHelperExtensions.FindTerm( - (JsonArray)taxonomy.Content["TaxonomyPart"]["Terms"], - termContentItemId); - - if (term is not null) - { - terms.Add(term); - } + terms.Add(term); } - - return FluidValue.Create(terms, ctx.Options); } + + return FluidValue.Create(terms, ctx.Options); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Migrations.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Migrations.cs index 8de9361f9e3..114bce3e873 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Migrations.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Migrations.cs @@ -8,175 +8,174 @@ using OrchardCore.Taxonomies.Settings; using YesSql.Sql; -namespace OrchardCore.Taxonomies +namespace OrchardCore.Taxonomies; + +public sealed class Migrations : DataMigration { - public sealed class Migrations : DataMigration + private readonly IContentDefinitionManager _contentDefinitionManager; + + public Migrations(IContentDefinitionManager contentDefinitionManager) + { + _contentDefinitionManager = contentDefinitionManager; + } + + public async Task CreateAsync() { - private readonly IContentDefinitionManager _contentDefinitionManager; - - public Migrations(IContentDefinitionManager contentDefinitionManager) - { - _contentDefinitionManager = contentDefinitionManager; - } - - public async Task CreateAsync() - { - await _contentDefinitionManager.AlterTypeDefinitionAsync("Taxonomy", taxonomy => taxonomy - .Draftable() - .Versionable() - .Creatable() - .Listable() - .WithPart("TitlePart", part => part.WithPosition("1")) - .WithPart("AliasPart", part => part - .WithPosition("2") - .WithSettings(new AliasPartSettings - { - Pattern = "{{ Model.ContentItem | display_text | slugify }}" - })) - .WithPart("AutoroutePart", part => part - .WithPosition("3") - .WithSettings(new AutoroutePartSettings - { - Pattern = "{{ Model.ContentItem | display_text | slugify }}", - AllowRouteContainedItems = true - })) - .WithPart("TaxonomyPart", part => part.WithPosition("4")) - ); - - await SchemaBuilder.CreateMapIndexTableAsync(table => table - .Column("TaxonomyContentItemId", c => c.WithLength(26)) - .Column("ContentItemId", c => c.WithLength(26)) - .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) - .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) - .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) - .Column("TermContentItemId", column => column.WithLength(26)) - .Column("Published", c => c.WithDefault(true)) - .Column("Latest", c => c.WithDefault(false)) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TaxonomyIndex_DocumentId", - "DocumentId", - "TaxonomyContentItemId", - "ContentItemId", - "TermContentItemId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TaxonomyIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // Shortcut other migration steps on new content definition schemas. - return 5; - } - - // Migrate FieldSettings. This only needs to run on old content definition schemas. - // This code can be removed in a later version. - public async Task UpdateFrom1Async() - { - await _contentDefinitionManager.MigrateFieldSettingsAsync(); - return 2; - } - - // This code can be removed in a later version. - public async Task UpdateFrom2Async() - { - await _contentDefinitionManager.AlterTypeDefinitionAsync("Taxonomy", taxonomy => taxonomy - .WithPart("AutoroutePart", part => part - .WithPosition("3") - .WithSettings(new AutoroutePartSettings - { - Pattern = "{{ Model.ContentItem | display_text | slugify }}", - AllowRouteContainedItems = true - })) - .WithPart("TaxonomyPart", part => part.WithPosition("4")) - ); - - return 3; - } - - // This code can be removed in a later version. - public async Task UpdateFrom3Async() - { - // This step has been updated to also add these new columns. - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Published", c => c.WithDefault(true)) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Latest", c => c.WithDefault(false)) - ); - - // So that the new indexes can be fully created. - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TaxonomyIndex_DocumentId", - "DocumentId", - "TaxonomyContentItemId", - "ContentItemId", - "TermContentItemId", - "Published", - "Latest") - ); - - // The index in MySQL can accommodate up to 768 characters or 3072 bytes. - // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TaxonomyIndex_DocumentId_ContentType", - "DocumentId", - "ContentType(254)", - "ContentPart(254)", - "ContentField(254)", - "Published", - "Latest") - ); - - // We then shortcut the next migration step. - return 5; - } - - // This code can be removed in a later version. - public async Task UpdateFrom4Async() - { - // This step run only if the previous one was executed before - // it was updated, so here we also add the following columns. - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Published", c => c.WithDefault(true)) - ); - - await SchemaBuilder.AlterIndexTableAsync(table => table - .AddColumn("Latest", c => c.WithDefault(false)) - ); - - // But we create a separate index for these new columns. - await SchemaBuilder.AlterIndexTableAsync(table => table - .CreateIndex("IDX_TaxonomyIndex_DocumentId_Published", - "DocumentId", - "Published", - "Latest") - ); - - return 5; - } + await _contentDefinitionManager.AlterTypeDefinitionAsync("Taxonomy", taxonomy => taxonomy + .Draftable() + .Versionable() + .Creatable() + .Listable() + .WithPart("TitlePart", part => part.WithPosition("1")) + .WithPart("AliasPart", part => part + .WithPosition("2") + .WithSettings(new AliasPartSettings + { + Pattern = "{{ Model.ContentItem | display_text | slugify }}" + })) + .WithPart("AutoroutePart", part => part + .WithPosition("3") + .WithSettings(new AutoroutePartSettings + { + Pattern = "{{ Model.ContentItem | display_text | slugify }}", + AllowRouteContainedItems = true + })) + .WithPart("TaxonomyPart", part => part.WithPosition("4")) + ); + + await SchemaBuilder.CreateMapIndexTableAsync(table => table + .Column("TaxonomyContentItemId", c => c.WithLength(26)) + .Column("ContentItemId", c => c.WithLength(26)) + .Column("ContentType", column => column.WithLength(ContentItemIndex.MaxContentTypeSize)) + .Column("ContentPart", column => column.WithLength(ContentItemIndex.MaxContentPartSize)) + .Column("ContentField", column => column.WithLength(ContentItemIndex.MaxContentFieldSize)) + .Column("TermContentItemId", column => column.WithLength(26)) + .Column("Published", c => c.WithDefault(true)) + .Column("Latest", c => c.WithDefault(false)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TaxonomyIndex_DocumentId", + "DocumentId", + "TaxonomyContentItemId", + "ContentItemId", + "TermContentItemId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TaxonomyIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // Shortcut other migration steps on new content definition schemas. + return 5; } - internal sealed class AliasPartSettings + // Migrate FieldSettings. This only needs to run on old content definition schemas. + // This code can be removed in a later version. + public async Task UpdateFrom1Async() { - public string Pattern { get; set; } + await _contentDefinitionManager.MigrateFieldSettingsAsync(); + return 2; } - internal sealed class AutoroutePartSettings + // This code can be removed in a later version. + public async Task UpdateFrom2Async() { - public string Pattern { get; set; } - public bool AllowRouteContainedItems { get; set; } + await _contentDefinitionManager.AlterTypeDefinitionAsync("Taxonomy", taxonomy => taxonomy + .WithPart("AutoroutePart", part => part + .WithPosition("3") + .WithSettings(new AutoroutePartSettings + { + Pattern = "{{ Model.ContentItem | display_text | slugify }}", + AllowRouteContainedItems = true + })) + .WithPart("TaxonomyPart", part => part.WithPosition("4")) + ); + + return 3; } + + // This code can be removed in a later version. + public async Task UpdateFrom3Async() + { + // This step has been updated to also add these new columns. + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Published", c => c.WithDefault(true)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Latest", c => c.WithDefault(false)) + ); + + // So that the new indexes can be fully created. + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TaxonomyIndex_DocumentId", + "DocumentId", + "TaxonomyContentItemId", + "ContentItemId", + "TermContentItemId", + "Published", + "Latest") + ); + + // The index in MySQL can accommodate up to 768 characters or 3072 bytes. + // DocumentId (2) + ContentType (254) + ContentPart (254) + ContentField (254) + Published and Latest (1) = 765 (< 768). + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TaxonomyIndex_DocumentId_ContentType", + "DocumentId", + "ContentType(254)", + "ContentPart(254)", + "ContentField(254)", + "Published", + "Latest") + ); + + // We then shortcut the next migration step. + return 5; + } + + // This code can be removed in a later version. + public async Task UpdateFrom4Async() + { + // This step run only if the previous one was executed before + // it was updated, so here we also add the following columns. + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Published", c => c.WithDefault(true)) + ); + + await SchemaBuilder.AlterIndexTableAsync(table => table + .AddColumn("Latest", c => c.WithDefault(false)) + ); + + // But we create a separate index for these new columns. + await SchemaBuilder.AlterIndexTableAsync(table => table + .CreateIndex("IDX_TaxonomyIndex_DocumentId_Published", + "DocumentId", + "Published", + "Latest") + ); + + return 5; + } +} + +internal sealed class AliasPartSettings +{ + public string Pattern { get; set; } +} + +internal sealed class AutoroutePartSettings +{ + public string Pattern { get; set; } + public bool AllowRouteContainedItems { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TaxonomyPart.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TaxonomyPart.cs index 905248df884..d5bd6fceaf1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TaxonomyPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TaxonomyPart.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using OrchardCore.ContentManagement; -namespace OrchardCore.Taxonomies.Models +namespace OrchardCore.Taxonomies.Models; + +// This part is added automatically to all taxonomies +public class TaxonomyPart : ContentPart { - // This part is added automatically to all taxonomies - public class TaxonomyPart : ContentPart - { - public string TermContentType { get; set; } - public List Terms { get; set; } = []; - } + public string TermContentType { get; set; } + public List Terms { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TermPart.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TermPart.cs index 796bb8f7fc6..a36030164ea 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TermPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Models/TermPart.cs @@ -1,10 +1,9 @@ using OrchardCore.ContentManagement; -namespace OrchardCore.Taxonomies.Models +namespace OrchardCore.Taxonomies.Models; + +// This part is added automatically to all terms +public class TermPart : ContentPart { - // This part is added automatically to all terms - public class TermPart : ContentPart - { - public string TaxonomyContentItemId { get; set; } - } + public string TaxonomyContentItemId { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Services/TaxonomyContentsAdminListFilter.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Services/TaxonomyContentsAdminListFilter.cs index 922cdda0e65..3398e1ad3e0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Services/TaxonomyContentsAdminListFilter.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Services/TaxonomyContentsAdminListFilter.cs @@ -10,45 +10,44 @@ using OrchardCore.Taxonomies.ViewModels; using YesSql; -namespace OrchardCore.Taxonomies.Services +namespace OrchardCore.Taxonomies.Services; + +// TODO Create a Terms Index independent of the standard index, which can index taxonomy terms by their display text +// Refer https://github.com/OrchardCMS/OrchardCore/issues/5214 +// This could then be migrated to use the filter parser. +public class TaxonomyContentsAdminListFilter : IContentsAdminListFilter { - // TODO Create a Terms Index independent of the standard index, which can index taxonomy terms by their display text - // Refer https://github.com/OrchardCMS/OrchardCore/issues/5214 - // This could then be migrated to use the filter parser. - public class TaxonomyContentsAdminListFilter : IContentsAdminListFilter + private readonly ISiteService _siteService; + + public TaxonomyContentsAdminListFilter(ISiteService siteService) { - private readonly ISiteService _siteService; + _siteService = siteService; + } - public TaxonomyContentsAdminListFilter(ISiteService siteService) + public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) + { + var settings = await _siteService.GetSettingsAsync(); + foreach (var contentItemId in settings.TaxonomyContentItemIds) { - _siteService = siteService; - } + var viewModel = new TaxonomyContentsAdminFilterViewModel(); + await updater.TryUpdateModelAsync(viewModel, "Taxonomy" + contentItemId); - public async Task FilterAsync(ContentOptionsViewModel model, IQuery query, IUpdateModel updater) - { - var settings = await _siteService.GetSettingsAsync(); - foreach (var contentItemId in settings.TaxonomyContentItemIds) + // Show all items categorized by the taxonomy + if (!string.IsNullOrEmpty(viewModel.SelectedContentItemId)) { - var viewModel = new TaxonomyContentsAdminFilterViewModel(); - await updater.TryUpdateModelAsync(viewModel, "Taxonomy" + contentItemId); - - // Show all items categorized by the taxonomy - if (!string.IsNullOrEmpty(viewModel.SelectedContentItemId)) + if (viewModel.SelectedContentItemId.StartsWith("Taxonomy:", StringComparison.OrdinalIgnoreCase)) + { + viewModel.SelectedContentItemId = viewModel.SelectedContentItemId[9..]; + query.All( + x => query.With(x => x.TaxonomyContentItemId == viewModel.SelectedContentItemId) + ); + } + else if (viewModel.SelectedContentItemId.StartsWith("Term:", StringComparison.OrdinalIgnoreCase)) { - if (viewModel.SelectedContentItemId.StartsWith("Taxonomy:", StringComparison.OrdinalIgnoreCase)) - { - viewModel.SelectedContentItemId = viewModel.SelectedContentItemId[9..]; - query.All( - x => query.With(x => x.TaxonomyContentItemId == viewModel.SelectedContentItemId) - ); - } - else if (viewModel.SelectedContentItemId.StartsWith("Term:", StringComparison.OrdinalIgnoreCase)) - { - viewModel.SelectedContentItemId = viewModel.SelectedContentItemId[5..]; - query.All( - x => query.With(x => x.TermContentItemId == viewModel.SelectedContentItemId) - ); - } + viewModel.SelectedContentItemId = viewModel.SelectedContentItemId[5..]; + query.All( + x => query.With(x => x.TermContentItemId == viewModel.SelectedContentItemId) + ); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettings.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettings.cs index b48bd93141e..d64e6a98a2e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettings.cs @@ -1,7 +1,6 @@ -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +public class TaxonomyContentsAdminListSettings { - public class TaxonomyContentsAdminListSettings - { - public string[] TaxonomyContentItemIds { get; set; } = []; - } + public string[] TaxonomyContentItemIds { get; set; } = []; } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettingsDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettingsDisplayDriver.cs index f6a76d68360..92f4cd1e069 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettingsDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyContentsAdminListSettingsDisplayDriver.cs @@ -12,62 +12,61 @@ using OrchardCore.Taxonomies.ViewModels; using YesSql; -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +public sealed class TaxonomyContentsAdminListSettingsDisplayDriver : SiteDisplayDriver { - public sealed class TaxonomyContentsAdminListSettingsDisplayDriver : SiteDisplayDriver + public const string GroupId = "taxonomyContentsAdminList"; + + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IAuthorizationService _authorizationService; + private readonly YesSql.ISession _session; + + public TaxonomyContentsAdminListSettingsDisplayDriver( + IHttpContextAccessor httpContextAccessor, + IAuthorizationService authorizationService, + YesSql.ISession session) { - public const string GroupId = "taxonomyContentsAdminList"; + _httpContextAccessor = httpContextAccessor; + _authorizationService = authorizationService; + _session = session; + } - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IAuthorizationService _authorizationService; - private readonly YesSql.ISession _session; + protected override string SettingsGroupId + => GroupId; - public TaxonomyContentsAdminListSettingsDisplayDriver( - IHttpContextAccessor httpContextAccessor, - IAuthorizationService authorizationService, - YesSql.ISession session) + public override async Task EditAsync(ISite site, TaxonomyContentsAdminListSettings settings, BuildEditorContext context) + { + var user = _httpContextAccessor.HttpContext?.User; + if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageTaxonomies)) { - _httpContextAccessor = httpContextAccessor; - _authorizationService = authorizationService; - _session = session; + return null; } - protected override string SettingsGroupId - => GroupId; + var taxonomies = await _session.Query(q => q.ContentType == "Taxonomy" && q.Published).ListAsync(); - public override async Task EditAsync(ISite site, TaxonomyContentsAdminListSettings settings, BuildEditorContext context) + var entries = taxonomies.Select(x => new TaxonomyEntry { - var user = _httpContextAccessor.HttpContext?.User; - if (!await _authorizationService.AuthorizeAsync(user, Permissions.ManageTaxonomies)) - { - return null; - } - - var taxonomies = await _session.Query(q => q.ContentType == "Taxonomy" && q.Published).ListAsync(); - - var entries = taxonomies.Select(x => new TaxonomyEntry - { - DisplayText = x.DisplayText, - ContentItemId = x.ContentItemId, - IsChecked = settings.TaxonomyContentItemIds.Any(id => string.Equals(x.ContentItemId, id, StringComparison.OrdinalIgnoreCase)) - }).ToArray(); + DisplayText = x.DisplayText, + ContentItemId = x.ContentItemId, + IsChecked = settings.TaxonomyContentItemIds.Any(id => string.Equals(x.ContentItemId, id, StringComparison.OrdinalIgnoreCase)) + }).ToArray(); - return Initialize("TaxonomyContentsAdminListSettings_Edit", model => - { - model.TaxonomyEntries = entries; - }).Location("Content:2") - .OnGroup(SettingsGroupId); - } - - public override async Task UpdateAsync(ISite site, TaxonomyContentsAdminListSettings settings, UpdateEditorContext context) + return Initialize("TaxonomyContentsAdminListSettings_Edit", model => { - var model = new TaxonomyContentsAdminListSettingsViewModel(); + model.TaxonomyEntries = entries; + }).Location("Content:2") + .OnGroup(SettingsGroupId); + } - await context.Updater.TryUpdateModelAsync(model, Prefix); + public override async Task UpdateAsync(ISite site, TaxonomyContentsAdminListSettings settings, UpdateEditorContext context) + { + var model = new TaxonomyContentsAdminListSettingsViewModel(); - settings.TaxonomyContentItemIds = model.TaxonomyEntries.Where(e => e.IsChecked).Select(e => e.ContentItemId).ToArray(); + await context.Updater.TryUpdateModelAsync(model, Prefix); - return await EditAsync(site, settings, context); - } + settings.TaxonomyContentItemIds = model.TaxonomyEntries.Where(e => e.IsChecked).Select(e => e.ContentItemId).ToArray(); + + return await EditAsync(site, settings, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettings.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettings.cs index 9d5241afdce..29d2fc32c28 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettings.cs @@ -1,32 +1,31 @@ -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +public class TaxonomyFieldSettings { - public class TaxonomyFieldSettings - { - public string Hint { get; set; } + public string Hint { get; set; } - /// - /// Whether a selection is required. - /// - public bool Required { get; set; } + /// + /// Whether a selection is required. + /// + public bool Required { get; set; } - /// - /// The content item id of the taxonomy to choose from. - /// - public string TaxonomyContentItemId { get; set; } + /// + /// The content item id of the taxonomy to choose from. + /// + public string TaxonomyContentItemId { get; set; } - /// - /// Whether the user can select only one term or not. - /// - public bool Unique { get; set; } + /// + /// Whether the user can select only one term or not. + /// + public bool Unique { get; set; } - /// - /// Whether the user can only select leaves in the taxonomy. - /// - public bool LeavesOnly { get; set; } + /// + /// Whether the user can only select leaves in the taxonomy. + /// + public bool LeavesOnly { get; set; } - /// - /// Whether the field allows the user to add new Terms to the taxonomy (similar to tags). - /// - public bool Open { get; set; } - } + /// + /// Whether the field allows the user to add new Terms to the taxonomy (similar to tags). + /// + public bool Open { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettingsDriver.cs index af748ab2b87..59e5c6e1de0 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldSettingsDriver.cs @@ -6,34 +6,33 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +public sealed class TaxonomyFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TaxonomyFieldSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("TaxonomyFieldSettings_Edit", model => { - return Initialize("TaxonomyFieldSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Hint = settings.Hint; - model.Required = settings.Required; - model.TaxonomyContentItemId = settings.TaxonomyContentItemId; - model.Unique = settings.Unique; - model.LeavesOnly = settings.LeavesOnly; - model.Open = settings.Open; - }).Location("Content"); - } + model.Hint = settings.Hint; + model.Required = settings.Required; + model.TaxonomyContentItemId = settings.TaxonomyContentItemId; + model.Unique = settings.Unique; + model.LeavesOnly = settings.LeavesOnly; + model.Open = settings.Open; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) - { - var model = new TaxonomyFieldSettings(); + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + var model = new TaxonomyFieldSettings(); - await context.Updater.TryUpdateModelAsync(model, Prefix); + await context.Updater.TryUpdateModelAsync(model, Prefix); - context.Builder.WithSettings(model); + context.Builder.WithSettings(model); - return await EditAsync(partFieldDefinition, context); - } + return await EditAsync(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettings.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettings.cs index 9a823efb3d4..afac90d0213 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettings.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettings.cs @@ -1,17 +1,16 @@ using System.ComponentModel; -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +/// +/// When transitioning a tag to a taxonomy editor these settings will need to be reset. +/// +// Despite being similar settings, we want different defaults when creating a tag editor. +public class TaxonomyFieldTagsEditorSettings { /// - /// When transitioning a tag to a taxonomy editor these settings will need to be reset. + /// Whether the field allows the user to add new tags to the taxonomy. /// - // Despite being similar settings, we want different defaults when creating a tag editor. - public class TaxonomyFieldTagsEditorSettings - { - /// - /// Whether the field allows the user to add new tags to the taxonomy. - /// - [DefaultValue(true)] - public bool Open { get; set; } = true; - } + [DefaultValue(true)] + public bool Open { get; set; } = true; } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettingsDriver.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettingsDriver.cs index e5b88d45d56..a3a67a3ea9f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettingsDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Settings/TaxonomyFieldTagsEditorSettingsDriver.cs @@ -6,32 +6,31 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.Settings +namespace OrchardCore.Taxonomies.Settings; + +public sealed class TaxonomyFieldTagsEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver { - public sealed class TaxonomyFieldTagsEditorSettingsDriver : ContentPartFieldDefinitionDisplayDriver + public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) { - public override IDisplayResult Edit(ContentPartFieldDefinition partFieldDefinition, BuildEditorContext context) + return Initialize("TaxonomyFieldTagsEditorSettings_Edit", model => { - return Initialize("TaxonomyFieldTagsEditorSettings_Edit", model => - { - var settings = partFieldDefinition.Settings.ToObject(); + var settings = partFieldDefinition.Settings.ToObject(); - model.Open = settings.Open; - }).Location("Content"); - } + model.Open = settings.Open; + }).Location("Content"); + } - public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + public override async Task UpdateAsync(ContentPartFieldDefinition partFieldDefinition, UpdatePartFieldEditorContext context) + { + if (partFieldDefinition.Editor() == "Tags") { - if (partFieldDefinition.Editor() == "Tags") - { - var model = new TaxonomyFieldTagsEditorSettings(); - - await context.Updater.TryUpdateModelAsync(model, Prefix); + var model = new TaxonomyFieldTagsEditorSettings(); - context.Builder.WithSettings(model); - } + await context.Updater.TryUpdateModelAsync(model, Prefix); - return Edit(partFieldDefinition, context); + context.Builder.WithSettings(model); } + + return Edit(partFieldDefinition, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Startup.cs index 16afbbb3819..12bd93bce60 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/Startup.cs @@ -30,85 +30,84 @@ using OrchardCore.Taxonomies.Settings; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies +namespace OrchardCore.Taxonomies; + +public sealed class Startup : StartupBase { - public sealed class Startup : StartupBase + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) + services.Configure(o => { - services.Configure(o => - { - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - o.MemberAccessStrategy.Register(); - }) - .AddLiquidFilter("inherited_terms") - .AddLiquidFilter("taxonomy_terms"); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + o.MemberAccessStrategy.Register(); + }) + .AddLiquidFilter("inherited_terms") + .AddLiquidFilter("taxonomy_terms"); - services.AddDataMigration(); - services.AddScoped(); - services.AddScoped(); + services.AddDataMigration(); + services.AddScoped(); + services.AddScoped(); - // Taxonomy Part - services.AddContentPart() - .UseDisplayDriver() - .AddHandler(); + // Taxonomy Part + services.AddContentPart() + .UseDisplayDriver() + .AddHandler(); - // Taxonomy Field - services.AddContentField() - .UseDisplayDriver(d => !string.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase)) - .AddHandler(); + // Taxonomy Field + services.AddContentField() + .UseDisplayDriver(d => !string.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase)) + .AddHandler(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); - // Taxonomy Tags Display Mode and Editor. - services.AddContentField() - .UseDisplayDriver(d => string.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase)); + // Taxonomy Tags Display Mode and Editor. + services.AddContentField() + .UseDisplayDriver(d => string.Equals(d, "Tags", StringComparison.OrdinalIgnoreCase)); - services.AddScoped(); + services.AddScoped(); - services.AddScopedIndexProvider(); + services.AddScopedIndexProvider(); - // Terms. - services.AddContentPart(); - services.AddScoped(); - services.AddScoped(); - } + // Terms. + services.AddContentPart(); + services.AddScoped(); + services.AddScoped(); } +} - [Feature("OrchardCore.Taxonomies.ContentsAdminList")] - public sealed class ContentsAdminListStartup : StartupBase +[Feature("OrchardCore.Taxonomies.ContentsAdminList")] +public sealed class ContentsAdminListStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddScoped(); - services.AddScoped, TaxonomyContentsAdminListDisplayDriver>(); + services.AddScoped(); + services.AddScoped, TaxonomyContentsAdminListDisplayDriver>(); - services.AddScoped(); - services.AddScoped, TaxonomyContentsAdminListSettingsDisplayDriver>(); - } + services.AddScoped(); + services.AddScoped, TaxonomyContentsAdminListSettingsDisplayDriver>(); } +} - [Feature("OrchardCore.Taxonomies.ContentsAdminList")] - [RequireFeatures("OrchardCore.Deployment")] - public sealed class ContentsAdminListDeploymentStartup : StartupBase +[Feature("OrchardCore.Taxonomies.ContentsAdminList")] +[RequireFeatures("OrchardCore.Deployment")] +public sealed class ContentsAdminListDeploymentStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddSiteSettingsPropertyDeploymentStep(S => S["Taxonomy Filters settings"], S => S["Exports the Taxonomy filters settings."]); - } + services.AddSiteSettingsPropertyDeploymentStep(S => S["Taxonomy Filters settings"], S => S["Exports the Taxonomy filters settings."]); } +} - [RequireFeatures("OrchardCore.Apis.GraphQL")] - public sealed class GraphQLStartup : StartupBase +[RequireFeatures("OrchardCore.Apis.GraphQL")] +public sealed class GraphQLStartup : StartupBase +{ + public override void ConfigureServices(IServiceCollection services) { - public override void ConfigureServices(IServiceCollection services) - { - services.AddObjectGraphType(); - services.AddObjectGraphType(); - } + services.AddObjectGraphType(); + services.AddObjectGraphType(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/TermShapes.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/TermShapes.cs index bbd5edeaec5..cfce135932b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/TermShapes.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/TermShapes.cs @@ -12,308 +12,307 @@ using OrchardCore.Taxonomies.Models; using OrchardCore.Taxonomies.ViewModels; -namespace OrchardCore.Taxonomies +namespace OrchardCore.Taxonomies; + +public class TermShapes : ShapeTableProvider { - public class TermShapes : ShapeTableProvider + public override ValueTask DiscoverAsync(ShapeTableBuilder builder) { - public override ValueTask DiscoverAsync(ShapeTableBuilder builder) - { - // Add standard alternates to a TermPart because it is rendered by a content display driver not a part display driver. - builder.Describe("TermPart") - .OnDisplaying(context => + // Add standard alternates to a TermPart because it is rendered by a content display driver not a part display driver. + builder.Describe("TermPart") + .OnDisplaying(context => + { + var viewModel = context.Shape as TermPartViewModel; + + var contentType = viewModel?.ContentItem?.ContentType; + var displayTypes = new[] { string.Empty, "_" + context.Shape.Metadata.DisplayType }; + + // [ShapeType]_[DisplayType], e.g. TermPart.Summary, TermPart.Detail. + context.Shape.Metadata.Alternates.Add($"TermPart_{context.Shape.Metadata.DisplayType}"); + + foreach (var displayType in displayTypes) + { + // [ContentType]_[DisplayType]__[PartType], e.g. Category-TermPart, Category-TermPart.Detail. + context.Shape.Metadata.Alternates.Add($"{contentType}{displayType}__TermPart"); + } + }); + + builder.Describe("Term") + .OnProcessing(async context => + { + var termShape = context.Shape; + var identifier = termShape.GetProperty("TaxonomyContentItemId") ?? termShape.GetProperty("Alias"); + + if (string.IsNullOrEmpty(identifier)) { - var viewModel = context.Shape as TermPartViewModel; + return; + } - var contentType = viewModel?.ContentItem?.ContentType; - var displayTypes = new[] { string.Empty, "_" + context.Shape.Metadata.DisplayType }; + termShape.Classes.Add("term"); - // [ShapeType]_[DisplayType], e.g. TermPart.Summary, TermPart.Detail. - context.Shape.Metadata.Alternates.Add($"TermPart_{context.Shape.Metadata.DisplayType}"); + // Term population is executed when processing the shape so that its value + // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor + // events and thus this code can be cached. - foreach (var displayType in displayTypes) - { - // [ContentType]_[DisplayType]__[PartType], e.g. Category-TermPart, Category-TermPart.Detail. - context.Shape.Metadata.Alternates.Add($"{contentType}{displayType}__TermPart"); - } - }); + var shapeFactory = context.ServiceProvider.GetRequiredService(); + var contentManager = context.ServiceProvider.GetRequiredService(); + var handleManager = context.ServiceProvider.GetRequiredService(); - builder.Describe("Term") - .OnProcessing(async context => + var taxonomyContentItemId = termShape.TryGetProperty("Alias", out object alias) && alias != null + ? await handleManager.GetContentItemIdAsync(alias.ToString()) + : termShape.Properties["TaxonomyContentItemId"].ToString(); + + if (taxonomyContentItemId == null) { - var termShape = context.Shape; - var identifier = termShape.GetProperty("TaxonomyContentItemId") ?? termShape.GetProperty("Alias"); + return; + } - if (string.IsNullOrEmpty(identifier)) - { - return; - } + var taxonomyContentItem = await contentManager.GetAsync(taxonomyContentItemId); - termShape.Classes.Add("term"); + if (taxonomyContentItem == null) + { + return; + } - // Term population is executed when processing the shape so that its value - // can be cached. IShapeDisplayEvents is called before the ShapeDescriptor - // events and thus this code can be cached. + termShape.Properties["TaxonomyContentItem"] = taxonomyContentItem; + termShape.Properties["TaxonomyName"] = taxonomyContentItem.DisplayText; - var shapeFactory = context.ServiceProvider.GetRequiredService(); - var contentManager = context.ServiceProvider.GetRequiredService(); - var handleManager = context.ServiceProvider.GetRequiredService(); + var taxonomyPart = taxonomyContentItem.As(); + if (taxonomyPart == null) + { + return; + } - var taxonomyContentItemId = termShape.TryGetProperty("Alias", out object alias) && alias != null - ? await handleManager.GetContentItemIdAsync(alias.ToString()) - : termShape.Properties["TaxonomyContentItemId"].ToString(); + // When a TermContentItemId is provided render the term and its child terms. + var level = 0; + List termItems = null; + var termContentItemId = termShape.GetProperty("TermContentItemId"); + if (!string.IsNullOrEmpty(termContentItemId)) + { + level = FindTerm((JsonArray)taxonomyContentItem.Content.TaxonomyPart.Terms, termContentItemId, level, out var termContentItem); - if (taxonomyContentItemId == null) + if (termContentItem == null) { return; } - var taxonomyContentItem = await contentManager.GetAsync(taxonomyContentItemId); + termItems = + [ + termContentItem + ]; + } + else + { + termItems = taxonomyPart.Terms; + } - if (taxonomyContentItem == null) - { - return; - } + if (termItems == null) + { + return; + } - termShape.Properties["TaxonomyContentItem"] = taxonomyContentItem; - termShape.Properties["TaxonomyName"] = taxonomyContentItem.DisplayText; + var differentiator = FormatName(termShape.GetProperty("TaxonomyName")); - var taxonomyPart = taxonomyContentItem.As(); - if (taxonomyPart == null) - { - return; - } + if (!string.IsNullOrEmpty(differentiator)) + { + // Term__[Differentiator] e.g. Term-Categories, Term-Tags. + termShape.Metadata.Alternates.Add("Term__" + differentiator); + termShape.Metadata.Differentiator = differentiator; + termShape.Classes.Add(("term-" + differentiator).HtmlClassify()); + } - // When a TermContentItemId is provided render the term and its child terms. - var level = 0; - List termItems = null; - var termContentItemId = termShape.GetProperty("TermContentItemId"); - if (!string.IsNullOrEmpty(termContentItemId)) - { - level = FindTerm((JsonArray)taxonomyContentItem.Content.TaxonomyPart.Terms, termContentItemId, level, out var termContentItem); + termShape.Classes.Add(("term-" + taxonomyPart.TermContentType).HtmlClassify()); - if (termContentItem == null) - { - return; - } + var encodedContentType = taxonomyPart.TermContentType.EncodeAlternateElement(); + // Term__[ContentType] e.g. Term-Category, Term-Tag. + termShape.Metadata.Alternates.Add("Term__" + encodedContentType); - termItems = - [ - termContentItem - ]; - } - else - { - termItems = taxonomyPart.Terms; - } + // The first level of term item shapes is created. + // Each other level is created when the term item is displayed. - if (termItems == null) + foreach (var termContentItem in termItems) + { + ContentItem[] childTerms = null; + if (((JsonObject)termContentItem.Content)["Terms"] is JsonArray termsArray) { - return; + childTerms = termsArray.ToObject(); } - var differentiator = FormatName(termShape.GetProperty("TaxonomyName")); - - if (!string.IsNullOrEmpty(differentiator)) + var shape = await shapeFactory.CreateAsync("TermItem", Arguments.From(new { - // Term__[Differentiator] e.g. Term-Categories, Term-Tags. - termShape.Metadata.Alternates.Add("Term__" + differentiator); - termShape.Metadata.Differentiator = differentiator; - termShape.Classes.Add(("term-" + differentiator).HtmlClassify()); - } + Level = level, + Term = termShape, + TermContentItem = termContentItem, + Terms = childTerms ?? [], + TaxonomyContentItem = taxonomyContentItem + })); + + shape.Metadata.Differentiator = differentiator; - termShape.Classes.Add(("term-" + taxonomyPart.TermContentType).HtmlClassify()); + // Don't use Items.Add() or the collection won't be sorted. + await termShape.AddAsync(shape); + } + }); - var encodedContentType = taxonomyPart.TermContentType.EncodeAlternateElement(); - // Term__[ContentType] e.g. Term-Category, Term-Tag. - termShape.Metadata.Alternates.Add("Term__" + encodedContentType); + builder.Describe("TermItem") + .OnDisplaying(async context => + { + var termItem = context.Shape; + var termShape = termItem.GetProperty("Term"); + var level = termItem.GetProperty("Level"); + var taxonomyContentItem = termItem.GetProperty("TaxonomyContentItem"); + var taxonomyPart = taxonomyContentItem.As(); + var differentiator = termItem.Metadata.Differentiator; - // The first level of term item shapes is created. - // Each other level is created when the term item is displayed. + var shapeFactory = context.ServiceProvider.GetRequiredService(); - foreach (var termContentItem in termItems) + if (termItem.GetProperty("Terms") != null) + { + foreach (var termContentItem in termItem.GetProperty("Terms")) { ContentItem[] childTerms = null; if (((JsonObject)termContentItem.Content)["Terms"] is JsonArray termsArray) { childTerms = termsArray.ToObject(); } - var shape = await shapeFactory.CreateAsync("TermItem", Arguments.From(new { - Level = level, - Term = termShape, + Level = level + 1, + TaxonomyContentItem = taxonomyContentItem, TermContentItem = termContentItem, - Terms = childTerms ?? [], - TaxonomyContentItem = taxonomyContentItem + Term = termShape, + Terms = childTerms ?? [] })); shape.Metadata.Differentiator = differentiator; // Don't use Items.Add() or the collection won't be sorted. - await termShape.AddAsync(shape); - } - }); - - builder.Describe("TermItem") - .OnDisplaying(async context => - { - var termItem = context.Shape; - var termShape = termItem.GetProperty("Term"); - var level = termItem.GetProperty("Level"); - var taxonomyContentItem = termItem.GetProperty("TaxonomyContentItem"); - var taxonomyPart = taxonomyContentItem.As(); - var differentiator = termItem.Metadata.Differentiator; - - var shapeFactory = context.ServiceProvider.GetRequiredService(); - - if (termItem.GetProperty("Terms") != null) - { - foreach (var termContentItem in termItem.GetProperty("Terms")) - { - ContentItem[] childTerms = null; - if (((JsonObject)termContentItem.Content)["Terms"] is JsonArray termsArray) - { - childTerms = termsArray.ToObject(); - } - var shape = await shapeFactory.CreateAsync("TermItem", Arguments.From(new - { - Level = level + 1, - TaxonomyContentItem = taxonomyContentItem, - TermContentItem = termContentItem, - Term = termShape, - Terms = childTerms ?? [] - })); - - shape.Metadata.Differentiator = differentiator; - - // Don't use Items.Add() or the collection won't be sorted. - await termItem.AddAsync(shape); - } + await termItem.AddAsync(shape); } + } - var encodedContentType = taxonomyPart.TermContentType.EncodeAlternateElement(); - - // TermItem__level__[level] e.g. TermItem-level-2. - termItem.Metadata.Alternates.Add("TermItem__level__" + level); + var encodedContentType = taxonomyPart.TermContentType.EncodeAlternateElement(); - // TermItem__[ContentType] e.g. TermItem-Category - // TermItem__[ContentType]__level__[level] e.g. TermItem-Category-level-2. - termItem.Metadata.Alternates.Add("TermItem__" + encodedContentType); - termItem.Metadata.Alternates.Add("TermItem__" + encodedContentType + "__level__" + level); + // TermItem__level__[level] e.g. TermItem-level-2. + termItem.Metadata.Alternates.Add("TermItem__level__" + level); - if (!string.IsNullOrEmpty(differentiator)) - { - // TermItem__[Differentiator] e.g. TermItem-Categories, TermItem-Travel. - // TermItem__[Differentiator]__level__[level] e.g. TermItem-Categories-level-2. - termItem.Metadata.Alternates.Add("TermItem__" + differentiator); - termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__level__" + level); - - // TermItem__[Differentiator]__[ContentType] e.g. TermItem-Categories-Category. - // TermItem__[Differentiator]__[ContentType]__level__[level] e.g. TermItem-Categories-Category-level-2. - termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__" + encodedContentType); - termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__" + encodedContentType + "__level__" + level); - } - }); + // TermItem__[ContentType] e.g. TermItem-Category + // TermItem__[ContentType]__level__[level] e.g. TermItem-Category-level-2. + termItem.Metadata.Alternates.Add("TermItem__" + encodedContentType); + termItem.Metadata.Alternates.Add("TermItem__" + encodedContentType + "__level__" + level); - builder.Describe("TermContentItem") - .OnDisplaying(displaying => + if (!string.IsNullOrEmpty(differentiator)) { - var termItem = displaying.Shape; - var level = termItem.GetProperty("Level"); - var differentiator = termItem.Metadata.Differentiator; + // TermItem__[Differentiator] e.g. TermItem-Categories, TermItem-Travel. + // TermItem__[Differentiator]__level__[level] e.g. TermItem-Categories-level-2. + termItem.Metadata.Alternates.Add("TermItem__" + differentiator); + termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__level__" + level); + + // TermItem__[Differentiator]__[ContentType] e.g. TermItem-Categories-Category. + // TermItem__[Differentiator]__[ContentType]__level__[level] e.g. TermItem-Categories-Category-level-2. + termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__" + encodedContentType); + termItem.Metadata.Alternates.Add("TermItem__" + differentiator + "__" + encodedContentType + "__level__" + level); + } + }); - var termContentItem = termItem.GetProperty("TermContentItem"); + builder.Describe("TermContentItem") + .OnDisplaying(displaying => + { + var termItem = displaying.Shape; + var level = termItem.GetProperty("Level"); + var differentiator = termItem.Metadata.Differentiator; - var encodedContentType = termContentItem.ContentItem.ContentType.EncodeAlternateElement(); + var termContentItem = termItem.GetProperty("TermContentItem"); - termItem.Metadata.Alternates.Add("TermContentItem__level__" + level); + var encodedContentType = termContentItem.ContentItem.ContentType.EncodeAlternateElement(); - // TermContentItem__[ContentType] e.g. TermContentItem-Category. - // TermContentItem__[ContentType]__level__[level] e.g. TermContentItem-Category-level-2. - termItem.Metadata.Alternates.Add("TermContentItem__" + encodedContentType); - termItem.Metadata.Alternates.Add("TermContentItem__" + encodedContentType + "__level__" + level); + termItem.Metadata.Alternates.Add("TermContentItem__level__" + level); - if (!string.IsNullOrEmpty(differentiator)) - { - // TermContentItem__[Differentiator] e.g. TermContentItem-Categories. - termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator); - // TermContentItem__[Differentiator]__level__[level] e.g. TermContentItem-Categories-level-2. - termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__level__" + level); - - // TermContentItem__[Differentiator]__[ContentType] e.g. TermContentItem-Categories-Category. - // TermContentItem__[Differentiator]__[ContentType] e.g. TermContentItem-Categories-Category-level-2. - termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__" + encodedContentType); - termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__" + encodedContentType + "__level__" + level); - } - }); + // TermContentItem__[ContentType] e.g. TermContentItem-Category. + // TermContentItem__[ContentType]__level__[level] e.g. TermContentItem-Category-level-2. + termItem.Metadata.Alternates.Add("TermContentItem__" + encodedContentType); + termItem.Metadata.Alternates.Add("TermContentItem__" + encodedContentType + "__level__" + level); - return ValueTask.CompletedTask; - } + if (!string.IsNullOrEmpty(differentiator)) + { + // TermContentItem__[Differentiator] e.g. TermContentItem-Categories. + termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator); + // TermContentItem__[Differentiator]__level__[level] e.g. TermContentItem-Categories-level-2. + termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__level__" + level); + + // TermContentItem__[Differentiator]__[ContentType] e.g. TermContentItem-Categories-Category. + // TermContentItem__[Differentiator]__[ContentType] e.g. TermContentItem-Categories-Category-level-2. + termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__" + encodedContentType); + termItem.Metadata.Alternates.Add("TermContentItem__" + differentiator + "__" + encodedContentType + "__level__" + level); + } + }); - private static int FindTerm(JsonArray termsArray, string termContentItemId, int level, out ContentItem contentItem) + return ValueTask.CompletedTask; + } + + private static int FindTerm(JsonArray termsArray, string termContentItemId, int level, out ContentItem contentItem) + { + foreach (var term in termsArray.Cast()) { - foreach (var term in termsArray.Cast()) + var contentItemId = term["ContentItemId"]?.ToString(); + if (contentItemId == termContentItemId) { - var contentItemId = term["ContentItemId"]?.ToString(); - if (contentItemId == termContentItemId) - { - contentItem = term.ToObject(); - return level; - } + contentItem = term.ToObject(); + return level; + } - if (term["Terms"] is JsonArray children) - { - level += 1; - level = FindTerm(children, termContentItemId, level, out var foundContentItem); + if (term["Terms"] is JsonArray children) + { + level += 1; + level = FindTerm(children, termContentItemId, level, out var foundContentItem); - if (foundContentItem != null) - { - contentItem = foundContentItem; - return level; - } + if (foundContentItem != null) + { + contentItem = foundContentItem; + return level; } } - contentItem = null; + } + contentItem = null; + + return level; + } - return level; + /// + /// Converts "foo-ba r" to "FooBaR". + /// + private static string FormatName(string name) + { + if (string.IsNullOrEmpty(name)) + { + return null; } - /// - /// Converts "foo-ba r" to "FooBaR". - /// - private static string FormatName(string name) + name = name.Trim(); + var nextIsUpper = true; + var result = new StringBuilder(name.Length); + for (var i = 0; i < name.Length; i++) { - if (string.IsNullOrEmpty(name)) + var c = name[i]; + + if (c == '-' || char.IsWhiteSpace(c)) { - return null; + nextIsUpper = true; + continue; } - name = name.Trim(); - var nextIsUpper = true; - var result = new StringBuilder(name.Length); - for (var i = 0; i < name.Length; i++) + if (nextIsUpper) { - var c = name[i]; - - if (c == '-' || char.IsWhiteSpace(c)) - { - nextIsUpper = true; - continue; - } - - if (nextIsUpper) - { - result.Append(c.ToString().ToUpper()); - } - else - { - result.Append(c); - } - - nextIsUpper = false; + result.Append(c.ToString().ToUpper()); + } + else + { + result.Append(c); } - return result.ToString(); + nextIsUpper = false; } + + return result.ToString(); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/CreatedTagViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/CreatedTagViewModel.cs index f7ef2c4a2db..413618122ac 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/CreatedTagViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/CreatedTagViewModel.cs @@ -1,8 +1,7 @@ -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class CreatedTagViewModel { - public class CreatedTagViewModel - { - public string ContentItemId { get; set; } - public string DisplayText { get; set; } - } + public string ContentItemId { get; set; } + public string DisplayText { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldTagsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldTagsViewModel.cs index ab072b504ce..a617d01dd97 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldTagsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldTagsViewModel.cs @@ -1,9 +1,8 @@ using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class DisplayTaxonomyFieldTagsViewModel : DisplayTaxonomyFieldViewModel { - public class DisplayTaxonomyFieldTagsViewModel : DisplayTaxonomyFieldViewModel - { - public string[] TagNames => Field.GetTagNames(); - } + public string[] TagNames => Field.GetTagNames(); } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldViewModel.cs index 6ae1987034c..71cfc7d560e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/DisplayTaxonomyFieldViewModel.cs @@ -2,14 +2,13 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class DisplayTaxonomyFieldViewModel { - public class DisplayTaxonomyFieldViewModel - { - public string TaxonomyContentItemId => Field.TaxonomyContentItemId; - public string[] TermContentItemIds => Field.TermContentItemIds; - public TaxonomyField Field { get; set; } - public ContentPart Part { get; set; } - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + public string TaxonomyContentItemId => Field.TaxonomyContentItemId; + public string[] TermContentItemIds => Field.TermContentItemIds; + public TaxonomyField Field { get; set; } + public ContentPart Part { get; set; } + public ContentPartFieldDefinition PartFieldDefinition { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTagTaxonomyFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTagTaxonomyFieldViewModel.cs index 619b7116b9f..84ec303b0ad 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTagTaxonomyFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTagTaxonomyFieldViewModel.cs @@ -3,33 +3,32 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class EditTagTaxonomyFieldViewModel { - public class EditTagTaxonomyFieldViewModel - { - public string TermContentItemIds { get; set; } + public string TermContentItemIds { get; set; } - [BindNever] - public string TagTermEntries { get; set; } + [BindNever] + public string TagTermEntries { get; set; } - [BindNever] - public ContentItem Taxonomy { get; set; } + [BindNever] + public ContentItem Taxonomy { get; set; } - [BindNever] - public TaxonomyField Field { get; set; } + [BindNever] + public TaxonomyField Field { get; set; } - [BindNever] - public ContentPart Part { get; set; } + [BindNever] + public ContentPart Part { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } +} - public class TagTermEntry - { - public bool Selected { get; set; } - public string ContentItemId { get; set; } - public string DisplayText { get; set; } - public bool IsLeaf { get; set; } - } +public class TagTermEntry +{ + public bool Selected { get; set; } + public string ContentItemId { get; set; } + public string DisplayText { get; set; } + public bool IsLeaf { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTaxonomyFieldViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTaxonomyFieldViewModel.cs index 468075ff76a..c1d53187431 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTaxonomyFieldViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/EditTaxonomyFieldViewModel.cs @@ -4,37 +4,36 @@ using OrchardCore.ContentManagement.Metadata.Models; using OrchardCore.Taxonomies.Fields; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class EditTaxonomyFieldViewModel { - public class EditTaxonomyFieldViewModel - { - public string UniqueValue { get; set; } - public List TermEntries { get; set; } = []; + public string UniqueValue { get; set; } + public List TermEntries { get; set; } = []; - [BindNever] - public ContentItem Taxonomy { get; set; } + [BindNever] + public ContentItem Taxonomy { get; set; } - [BindNever] - public TaxonomyField Field { get; set; } + [BindNever] + public TaxonomyField Field { get; set; } - [BindNever] - public ContentPart Part { get; set; } + [BindNever] + public ContentPart Part { get; set; } - [BindNever] - public ContentPartFieldDefinition PartFieldDefinition { get; set; } - } + [BindNever] + public ContentPartFieldDefinition PartFieldDefinition { get; set; } +} - public class TermEntry - { - [BindNever] - public ContentItem Term { get; set; } +public class TermEntry +{ + [BindNever] + public ContentItem Term { get; set; } - public bool Selected { get; set; } - public string ContentItemId { get; set; } + public bool Selected { get; set; } + public string ContentItemId { get; set; } - [BindNever] - public int Level { get; set; } + [BindNever] + public int Level { get; set; } - public bool IsLeaf { get; set; } - } + public bool IsLeaf { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminFilterViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminFilterViewModel.cs index 27e5a666081..d589d5ef95b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminFilterViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminFilterViewModel.cs @@ -2,16 +2,15 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Rendering; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class TaxonomyContentsAdminFilterViewModel { - public class TaxonomyContentsAdminFilterViewModel - { - public string SelectedContentItemId { get; set; } + public string SelectedContentItemId { get; set; } - [BindNever] - public string DisplayText { get; set; } + [BindNever] + public string DisplayText { get; set; } - [BindNever] - public List Taxonomies { get; set; } - } + [BindNever] + public List Taxonomies { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminListSettingsViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminListSettingsViewModel.cs index c616317e3e2..5abd82410bc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminListSettingsViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyContentsAdminListSettingsViewModel.cs @@ -1,14 +1,13 @@ -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class TaxonomyContentsAdminListSettingsViewModel { - public class TaxonomyContentsAdminListSettingsViewModel - { - public TaxonomyEntry[] TaxonomyEntries { get; set; } - } + public TaxonomyEntry[] TaxonomyEntries { get; set; } +} - public class TaxonomyEntry - { - public string DisplayText { get; set; } - public string ContentItemId { get; set; } - public bool IsChecked { get; set; } - } +public class TaxonomyEntry +{ + public string DisplayText { get; set; } + public string ContentItemId { get; set; } + public bool IsChecked { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartEditViewModel.cs index 217955af34f..ee0244a9f8e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartEditViewModel.cs @@ -1,15 +1,14 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.Taxonomies.Models; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class TaxonomyPartEditViewModel { - public class TaxonomyPartEditViewModel - { - public string Hierarchy { get; set; } + public string Hierarchy { get; set; } - public string TermContentType { get; set; } + public string TermContentType { get; set; } - [BindNever] - public TaxonomyPart TaxonomyPart { get; set; } - } + [BindNever] + public TaxonomyPart TaxonomyPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartViewModel.cs index f36ee178dad..a472d9b3354 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TaxonomyPartViewModel.cs @@ -2,16 +2,15 @@ using OrchardCore.ContentManagement; using OrchardCore.Taxonomies.Models; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class TaxonomyPartViewModel { - public class TaxonomyPartViewModel - { - public string TaxonomyContentItemId => ContentItem.ContentItemId; + public string TaxonomyContentItemId => ContentItem.ContentItemId; - [BindNever] - public ContentItem ContentItem { get; set; } + [BindNever] + public ContentItem ContentItem { get; set; } - [BindNever] - public TaxonomyPart TaxonomyPart { get; set; } - } + [BindNever] + public TaxonomyPart TaxonomyPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TermPartViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TermPartViewModel.cs index b8395baa4a8..10b60b30131 100644 --- a/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TermPartViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Taxonomies/ViewModels/TermPartViewModel.cs @@ -2,16 +2,15 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using OrchardCore.ContentManagement; -namespace OrchardCore.Taxonomies.ViewModels +namespace OrchardCore.Taxonomies.ViewModels; + +public class TermPartViewModel { - public class TermPartViewModel - { - public string TermContentItemId => ContentItem.ContentItemId; - public string TaxonomyContentItemId { get; set; } - public IEnumerable ContentItems { get; set; } - public dynamic Pager { get; set; } + public string TermContentItemId => ContentItem.ContentItemId; + public string TaxonomyContentItemId { get; set; } + public IEnumerable ContentItems { get; set; } + public dynamic Pager { get; set; } - [BindNever] - public ContentItem ContentItem { get; set; } - } + [BindNever] + public ContentItem ContentItem { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/AdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Templates/AdminMenu.cs index b15e981a595..e1aecd03a7f 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/AdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/AdminMenu.cs @@ -2,34 +2,33 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Templates +namespace OrchardCore.Templates; + +public sealed class AdminMenu : INavigationProvider { - public sealed class AdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Design"], design => design - .Add(S["Templates"], S["Templates"].PrefixPosition(), import => import - .Action("Index", "Template", "OrchardCore.Templates") - .Permission(Permissions.ManageTemplates) - .LocalNav() - ) - ); + builder + .Add(S["Design"], design => design + .Add(S["Templates"], S["Templates"].PrefixPosition(), import => import + .Action("Index", "Template", "OrchardCore.Templates") + .Permission(Permissions.ManageTemplates) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/AdminTemplatesAdminMenu.cs b/src/OrchardCore.Modules/OrchardCore.Templates/AdminTemplatesAdminMenu.cs index 08560028e79..7f6e7e2381a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/AdminTemplatesAdminMenu.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/AdminTemplatesAdminMenu.cs @@ -2,34 +2,33 @@ using Microsoft.Extensions.Localization; using OrchardCore.Navigation; -namespace OrchardCore.Templates +namespace OrchardCore.Templates; + +public sealed class AdminTemplatesAdminMenu : INavigationProvider { - public sealed class AdminTemplatesAdminMenu : INavigationProvider + internal readonly IStringLocalizer S; + + public AdminTemplatesAdminMenu(IStringLocalizer localizer) { - internal readonly IStringLocalizer S; + S = localizer; + } - public AdminTemplatesAdminMenu(IStringLocalizer localizer) + public Task BuildNavigationAsync(string name, NavigationBuilder builder) + { + if (!NavigationHelper.IsAdminMenu(name)) { - S = localizer; + return Task.CompletedTask; } - public Task BuildNavigationAsync(string name, NavigationBuilder builder) - { - if (!NavigationHelper.IsAdminMenu(name)) - { - return Task.CompletedTask; - } - - builder - .Add(S["Design"], design => design - .Add(S["Admin Templates"], S["Admin Templates"].PrefixPosition(), import => import - .Action("Admin", "Template", "OrchardCore.Templates") - .Permission(AdminTemplatesPermissions.ManageAdminTemplates) - .LocalNav() - ) - ); + builder + .Add(S["Design"], design => design + .Add(S["Admin Templates"], S["Admin Templates"].PrefixPosition(), import => import + .Action("Admin", "Template", "OrchardCore.Templates") + .Permission(AdminTemplatesPermissions.ManageAdminTemplates) + .LocalNav() + ) + ); - return Task.CompletedTask; - } + return Task.CompletedTask; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/PreviewController.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/PreviewController.cs index adc4723102d..44e302bcd37 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/PreviewController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/PreviewController.cs @@ -11,93 +11,92 @@ using OrchardCore.Settings; using OrchardCore.Templates.ViewModels; -namespace OrchardCore.Templates.Controllers +namespace OrchardCore.Templates.Controllers; + +public class PreviewController : Controller { - public class PreviewController : Controller + private readonly IContentManager _contentManager; + private readonly IContentHandleManager _contentHandleManager; + private readonly IContentItemDisplayManager _contentItemDisplayManager; + private readonly IAuthorizationService _authorizationService; + private readonly ISiteService _siteService; + private readonly IUpdateModelAccessor _updateModelAccessor; + private readonly string _homeUrl; + + public PreviewController( + IContentManager contentManager, + IContentHandleManager contentHandleManager, + IContentItemDisplayManager contentItemDisplayManager, + IAuthorizationService authorizationService, + ISiteService siteService, + IUpdateModelAccessor updateModelAccessor, + IHttpContextAccessor httpContextAccessor) { - private readonly IContentManager _contentManager; - private readonly IContentHandleManager _contentHandleManager; - private readonly IContentItemDisplayManager _contentItemDisplayManager; - private readonly IAuthorizationService _authorizationService; - private readonly ISiteService _siteService; - private readonly IUpdateModelAccessor _updateModelAccessor; - private readonly string _homeUrl; - - public PreviewController( - IContentManager contentManager, - IContentHandleManager contentHandleManager, - IContentItemDisplayManager contentItemDisplayManager, - IAuthorizationService authorizationService, - ISiteService siteService, - IUpdateModelAccessor updateModelAccessor, - IHttpContextAccessor httpContextAccessor) - { - _contentManager = contentManager; - _contentHandleManager = contentHandleManager; - _contentItemDisplayManager = contentItemDisplayManager; - _authorizationService = authorizationService; - _siteService = siteService; - _updateModelAccessor = updateModelAccessor; - _homeUrl = httpContextAccessor.HttpContext.Request.PathBase.Add("/"); - } + _contentManager = contentManager; + _contentHandleManager = contentHandleManager; + _contentItemDisplayManager = contentItemDisplayManager; + _authorizationService = authorizationService; + _siteService = siteService; + _updateModelAccessor = updateModelAccessor; + _homeUrl = httpContextAccessor.HttpContext.Request.PathBase.Add("/"); + } - public IActionResult Index() - { - return View(); - } + public IActionResult Index() + { + return View(); + } - [HttpPost] - public async Task Render() + [HttpPost] + public async Task Render() + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return this.ChallengeOrForbid(); - } + return this.ChallengeOrForbid(); + } - // Mark request as a `Preview` request so that drivers / handlers or underlying services can be aware of an active preview mode. - HttpContext.Features.Set(new ContentPreviewFeature()); + // Mark request as a `Preview` request so that drivers / handlers or underlying services can be aware of an active preview mode. + HttpContext.Features.Set(new ContentPreviewFeature()); - var name = Request.Form["Name"]; - var content = Request.Form["Content"]; + var name = Request.Form["Name"]; + var content = Request.Form["Content"]; - if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(content)) - { - HttpContext.Items["OrchardCore.PreviewTemplate"] = new TemplateViewModel { Name = name, Content = content }; - } + if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(content)) + { + HttpContext.Items["OrchardCore.PreviewTemplate"] = new TemplateViewModel { Name = name, Content = content }; + } - var handle = Request.Form["Handle"].ToString(); + var handle = Request.Form["Handle"].ToString(); - string contentItemId; + string contentItemId; - if (string.IsNullOrEmpty(handle) || handle == _homeUrl) - { - var homeRoute = (await _siteService.GetSiteSettingsAsync()).HomeRoute; - contentItemId = homeRoute["contentItemId"]?.ToString(); - } - else - { - var index = handle.IndexOf(_homeUrl, StringComparison.Ordinal); + if (string.IsNullOrEmpty(handle) || handle == _homeUrl) + { + var homeRoute = (await _siteService.GetSiteSettingsAsync()).HomeRoute; + contentItemId = homeRoute["contentItemId"]?.ToString(); + } + else + { + var index = handle.IndexOf(_homeUrl, StringComparison.Ordinal); - handle = (index < 0 ? handle : handle[_homeUrl.Length..]) - .ToUriComponents(UriFormat.SafeUnescaped); + handle = (index < 0 ? handle : handle[_homeUrl.Length..]) + .ToUriComponents(UriFormat.SafeUnescaped); - contentItemId = await _contentHandleManager.GetContentItemIdAsync(AutorouteConstants.SlugPrefix + handle); - } + contentItemId = await _contentHandleManager.GetContentItemIdAsync(AutorouteConstants.SlugPrefix + handle); + } - if (string.IsNullOrEmpty(contentItemId)) - { - return NotFound(); - } + if (string.IsNullOrEmpty(contentItemId)) + { + return NotFound(); + } - var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Published); - if (contentItem is null) - { - return NotFound(); - } + var contentItem = await _contentManager.GetAsync(contentItemId, VersionOptions.Published); + if (contentItem is null) + { + return NotFound(); + } - var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, "Detail"); + var model = await _contentItemDisplayManager.BuildDisplayAsync(contentItem, _updateModelAccessor.ModelUpdater, "Detail"); - return View(model); - } + return View(model); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/TemplateController.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/TemplateController.cs index 679c39b5e8f..a2a314c0e1a 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/TemplateController.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Controllers/TemplateController.cs @@ -18,390 +18,389 @@ using OrchardCore.Templates.Services; using OrchardCore.Templates.ViewModels; -namespace OrchardCore.Templates.Controllers +namespace OrchardCore.Templates.Controllers; + +[Admin("Templates/{action}/{name?}", "Templates.{action}")] +public class TemplateController : Controller { - [Admin("Templates/{action}/{name?}", "Templates.{action}")] - public class TemplateController : Controller + private const string _optionsSearch = "Options.Search"; + + private readonly IAuthorizationService _authorizationService; + private readonly TemplatesManager _templatesManager; + private readonly AdminTemplatesManager _adminTemplatesManager; + private readonly IShapeFactory _shapeFactory; + private readonly PagerOptions _pagerOptions; + private readonly INotifier _notifier; + + protected readonly IStringLocalizer S; + protected readonly IHtmlLocalizer H; + + public TemplateController( + IAuthorizationService authorizationService, + TemplatesManager templatesManager, + AdminTemplatesManager adminTemplatesManager, + IShapeFactory shapeFactory, + IOptions pagerOptions, + IStringLocalizer stringLocalizer, + IHtmlLocalizer htmlLocalizer, + INotifier notifier) + { + _authorizationService = authorizationService; + _templatesManager = templatesManager; + _adminTemplatesManager = adminTemplatesManager; + _shapeFactory = shapeFactory; + _pagerOptions = pagerOptions.Value; + _notifier = notifier; + S = stringLocalizer; + H = htmlLocalizer; + } + + public Task Admin(ContentOptions options, PagerParameters pagerParameters) + { + options.AdminTemplates = true; + + // Used to provide a different url such that the Admin Templates menu entry doesn't collide with the Templates ones. + return Index(options, pagerParameters); + } + + [Admin("Templates", "Templates.Index")] + public async Task Index(ContentOptions options, PagerParameters pagerParameters) { - private const string _optionsSearch = "Options.Search"; - - private readonly IAuthorizationService _authorizationService; - private readonly TemplatesManager _templatesManager; - private readonly AdminTemplatesManager _adminTemplatesManager; - private readonly IShapeFactory _shapeFactory; - private readonly PagerOptions _pagerOptions; - private readonly INotifier _notifier; - - protected readonly IStringLocalizer S; - protected readonly IHtmlLocalizer H; - - public TemplateController( - IAuthorizationService authorizationService, - TemplatesManager templatesManager, - AdminTemplatesManager adminTemplatesManager, - IShapeFactory shapeFactory, - IOptions pagerOptions, - IStringLocalizer stringLocalizer, - IHtmlLocalizer htmlLocalizer, - INotifier notifier) + if (!options.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) { - _authorizationService = authorizationService; - _templatesManager = templatesManager; - _adminTemplatesManager = adminTemplatesManager; - _shapeFactory = shapeFactory; - _pagerOptions = pagerOptions.Value; - _notifier = notifier; - S = stringLocalizer; - H = htmlLocalizer; + return Forbid(); } - public Task Admin(ContentOptions options, PagerParameters pagerParameters) + if (options.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) { - options.AdminTemplates = true; - - // Used to provide a different url such that the Admin Templates menu entry doesn't collide with the Templates ones. - return Index(options, pagerParameters); + return Forbid(); } - [Admin("Templates", "Templates.Index")] - public async Task Index(ContentOptions options, PagerParameters pagerParameters) - { - if (!options.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); + var templatesDocument = options.AdminTemplates + ? await _adminTemplatesManager.GetTemplatesDocumentAsync() + : await _templatesManager.GetTemplatesDocumentAsync() + ; - if (options.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) - { - return Forbid(); - } + var templates = templatesDocument.Templates.ToList(); - var pager = new Pager(pagerParameters, _pagerOptions.GetPageSize()); - var templatesDocument = options.AdminTemplates - ? await _adminTemplatesManager.GetTemplatesDocumentAsync() - : await _templatesManager.GetTemplatesDocumentAsync() - ; + if (!string.IsNullOrWhiteSpace(options.Search)) + { + templates = templates.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); + } - var templates = templatesDocument.Templates.ToList(); + var count = templates.Count; - if (!string.IsNullOrWhiteSpace(options.Search)) - { - templates = templates.Where(x => x.Key.Contains(options.Search, StringComparison.OrdinalIgnoreCase)).ToList(); - } + templates = templates.OrderBy(x => x.Key) + .Skip(pager.GetStartIndex()) + .Take(pager.PageSize).ToList(); - var count = templates.Count; + // Maintain previous route data when generating page links. + var routeData = new RouteData(); - templates = templates.OrderBy(x => x.Key) - .Skip(pager.GetStartIndex()) - .Take(pager.PageSize).ToList(); + if (!string.IsNullOrEmpty(options.Search)) + { + routeData.Values.TryAdd(_optionsSearch, options.Search); + } - // Maintain previous route data when generating page links. - var routeData = new RouteData(); + var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); + var model = new TemplateIndexViewModel + { + Templates = templates.Select(x => new TemplateEntry { Name = x.Key, Template = x.Value }).ToList(), + Options = options, + Pager = pagerShape + }; + + model.Options.ContentsBulkAction = + [ + new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), + ]; + + // The 'Admin' action redirect the user to the 'Index' action. + // To ensure we render the same 'Index' view in both cases, we have to explicitly specify the name of the view that should be rendered. + return View(nameof(Index), model); + } - if (!string.IsNullOrEmpty(options.Search)) - { - routeData.Values.TryAdd(_optionsSearch, options.Search); - } + [HttpPost, ActionName(nameof(Index))] + [FormValueRequired("submit.Filter")] + public ActionResult IndexFilterPOST(TemplateIndexViewModel model) + => RedirectToAction(nameof(Index), new RouteValueDictionary + { + { _optionsSearch, model.Options.Search } + }); - var pagerShape = await _shapeFactory.PagerAsync(pager, count, routeData); - var model = new TemplateIndexViewModel - { - Templates = templates.Select(x => new TemplateEntry { Name = x.Key, Template = x.Value }).ToList(), - Options = options, - Pager = pagerShape - }; - - model.Options.ContentsBulkAction = - [ - new SelectListItem(S["Delete"], nameof(ContentsBulkAction.Remove)), - ]; - - // The 'Admin' action redirect the user to the 'Index' action. - // To ensure we render the same 'Index' view in both cases, we have to explicitly specify the name of the view that should be rendered. - return View(nameof(Index), model); + public async Task Create(string name = null, bool adminTemplates = false, string returnUrl = null) + { + if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Index))] - [FormValueRequired("submit.Filter")] - public ActionResult IndexFilterPOST(TemplateIndexViewModel model) - => RedirectToAction(nameof(Index), new RouteValueDictionary - { - { _optionsSearch, model.Options.Search } - }); + if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) + { + return Forbid(); + } - public async Task Create(string name = null, bool adminTemplates = false, string returnUrl = null) + var model = new TemplateViewModel { - if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + AdminTemplates = adminTemplates, + Name = name + }; - if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) - { - return Forbid(); - } + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - var model = new TemplateViewModel - { - AdminTemplates = adminTemplates, - Name = name - }; + [HttpPost, ActionName(nameof(Create))] + public async Task CreatePost(TemplateViewModel model, string submit, string returnUrl = null) + { + if (!model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); + } - ViewData["ReturnUrl"] = returnUrl; - return View(model); + if (model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) + { + return Forbid(); } - [HttpPost, ActionName(nameof(Create))] - public async Task CreatePost(TemplateViewModel model, string submit, string returnUrl = null) + ViewData["ReturnUrl"] = returnUrl; + + if (ModelState.IsValid) { - if (!model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(TemplateViewModel.Name), S["The name is mandatory."]); } - - if (model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) + else if (string.IsNullOrWhiteSpace(model.Content)) { - return Forbid(); + ModelState.AddModelError(nameof(TemplateViewModel.Content), S["The content is mandatory."]); } - - ViewData["ReturnUrl"] = returnUrl; - - if (ModelState.IsValid) + else { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Name), S["The name is mandatory."]); - } - else if (string.IsNullOrWhiteSpace(model.Content)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Content), S["The content is mandatory."]); - } - else - { - var templatesDocument = model.AdminTemplates - ? await _adminTemplatesManager.GetTemplatesDocumentAsync() - : await _templatesManager.GetTemplatesDocumentAsync() - ; + var templatesDocument = model.AdminTemplates + ? await _adminTemplatesManager.GetTemplatesDocumentAsync() + : await _templatesManager.GetTemplatesDocumentAsync() + ; - if (templatesDocument.Templates.ContainsKey(model.Name)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Name), S["A template with the same name already exists."]); - } + if (templatesDocument.Templates.ContainsKey(model.Name)) + { + ModelState.AddModelError(nameof(TemplateViewModel.Name), S["A template with the same name already exists."]); } } + } - if (ModelState.IsValid) - { - var template = new Template { Content = model.Content, Description = model.Description }; + if (ModelState.IsValid) + { + var template = new Template { Content = model.Content, Description = model.Description }; - await (model.AdminTemplates - ? _adminTemplatesManager.UpdateTemplateAsync(model.Name, template) - : _templatesManager.UpdateTemplateAsync(model.Name, template) - ); + await (model.AdminTemplates + ? _adminTemplatesManager.UpdateTemplateAsync(model.Name, template) + : _templatesManager.UpdateTemplateAsync(model.Name, template) + ); - await _notifier.SuccessAsync(H["The \"{0}\" template has been created.", model.Name]); + await _notifier.SuccessAsync(H["The \"{0}\" template has been created.", model.Name]); - if (submit == "SaveAndContinue") - { - return RedirectToAction(nameof(Edit), new { name = model.Name, adminTemplates = model.AdminTemplates, returnUrl }); - } - else - { - return RedirectToReturnUrlOrIndex(returnUrl); - } + if (submit == "SaveAndContinue") + { + return RedirectToAction(nameof(Edit), new { name = model.Name, adminTemplates = model.AdminTemplates, returnUrl }); } + else + { + return RedirectToReturnUrlOrIndex(returnUrl); + } + } - // If we got this far, something failed, redisplay form. - return View(model); + // If we got this far, something failed, redisplay form. + return View(model); + } + + public async Task Edit(string name, bool adminTemplates = false, string returnUrl = null) + { + if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); } - public async Task Edit(string name, bool adminTemplates = false, string returnUrl = null) + if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) { - if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + return Forbid(); + } - if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) - { - return Forbid(); - } + var templatesDocument = adminTemplates + ? await _adminTemplatesManager.GetTemplatesDocumentAsync() + : await _templatesManager.GetTemplatesDocumentAsync() + ; - var templatesDocument = adminTemplates - ? await _adminTemplatesManager.GetTemplatesDocumentAsync() - : await _templatesManager.GetTemplatesDocumentAsync() - ; + if (!templatesDocument.Templates.TryGetValue(name, out var template)) + { + return RedirectToAction(nameof(Create), new { name, returnUrl }); + } - if (!templatesDocument.Templates.TryGetValue(name, out var template)) - { - return RedirectToAction(nameof(Create), new { name, returnUrl }); - } + var model = new TemplateViewModel + { + AdminTemplates = adminTemplates, + Name = name, + Content = template.Content, + Description = template.Description + }; + + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } - var model = new TemplateViewModel - { - AdminTemplates = adminTemplates, - Name = name, - Content = template.Content, - Description = template.Description - }; - - ViewData["ReturnUrl"] = returnUrl; - return View(model); + [HttpPost] + public async Task Edit(string sourceName, TemplateViewModel model, string submit, string returnUrl = null) + { + if (!model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); } - [HttpPost] - public async Task Edit(string sourceName, TemplateViewModel model, string submit, string returnUrl = null) + if (model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) { - if (!model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + return Forbid(); + } + + var templatesDocument = model.AdminTemplates + ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() + : await _templatesManager.LoadTemplatesDocumentAsync() + ; - if (model.AdminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) + if (ModelState.IsValid) + { + if (string.IsNullOrWhiteSpace(model.Name)) { - return Forbid(); + ModelState.AddModelError(nameof(TemplateViewModel.Name), S["The name is mandatory."]); } - - var templatesDocument = model.AdminTemplates - ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() - : await _templatesManager.LoadTemplatesDocumentAsync() - ; - - if (ModelState.IsValid) + else if (!model.Name.Equals(sourceName, StringComparison.OrdinalIgnoreCase) && templatesDocument.Templates.ContainsKey(model.Name)) { - if (string.IsNullOrWhiteSpace(model.Name)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Name), S["The name is mandatory."]); - } - else if (!model.Name.Equals(sourceName, StringComparison.OrdinalIgnoreCase) && templatesDocument.Templates.ContainsKey(model.Name)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Name), S["A template with the same name already exists."]); - } - else if (string.IsNullOrWhiteSpace(model.Content)) - { - ModelState.AddModelError(nameof(TemplateViewModel.Content), S["The content is mandatory."]); - } + ModelState.AddModelError(nameof(TemplateViewModel.Name), S["A template with the same name already exists."]); } - - if (!templatesDocument.Templates.ContainsKey(sourceName)) + else if (string.IsNullOrWhiteSpace(model.Content)) { - return NotFound(); + ModelState.AddModelError(nameof(TemplateViewModel.Content), S["The content is mandatory."]); } + } - if (ModelState.IsValid) - { - var template = new Template { Content = model.Content, Description = model.Description }; + if (!templatesDocument.Templates.ContainsKey(sourceName)) + { + return NotFound(); + } - await (model.AdminTemplates - ? _adminTemplatesManager.RemoveTemplateAsync(sourceName) - : _templatesManager.RemoveTemplateAsync(sourceName) - ); + if (ModelState.IsValid) + { + var template = new Template { Content = model.Content, Description = model.Description }; - await (model.AdminTemplates - ? _adminTemplatesManager.UpdateTemplateAsync(model.Name, template) - : _templatesManager.UpdateTemplateAsync(model.Name, template) - ); + await (model.AdminTemplates + ? _adminTemplatesManager.RemoveTemplateAsync(sourceName) + : _templatesManager.RemoveTemplateAsync(sourceName) + ); - if (submit != "SaveAndContinue") - { - return RedirectToReturnUrlOrIndex(returnUrl); - } + await (model.AdminTemplates + ? _adminTemplatesManager.UpdateTemplateAsync(model.Name, template) + : _templatesManager.UpdateTemplateAsync(model.Name, template) + ); + + if (submit != "SaveAndContinue") + { + return RedirectToReturnUrlOrIndex(returnUrl); } + } - // If we got this far, something failed, redisplay form. - ViewData["ReturnUrl"] = returnUrl; + // If we got this far, something failed, redisplay form. + ViewData["ReturnUrl"] = returnUrl; - // If the name was changed or removed, prevent a 404 or a failure on the next post. - model.Name = sourceName; + // If the name was changed or removed, prevent a 404 or a failure on the next post. + model.Name = sourceName; - return View(model); + return View(model); + } + + [HttpPost] + public async Task Delete(string name, string returnUrl, bool adminTemplates = false) + { + if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); } - [HttpPost] - public async Task Delete(string name, string returnUrl, bool adminTemplates = false) + if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) { - if (!adminTemplates && !await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + return Forbid(); + } - if (adminTemplates && !await _authorizationService.AuthorizeAsync(User, AdminTemplatesPermissions.ManageAdminTemplates)) - { - return Forbid(); - } + var templatesDocument = adminTemplates + ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() + : await _templatesManager.LoadTemplatesDocumentAsync(); - var templatesDocument = adminTemplates - ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() - : await _templatesManager.LoadTemplatesDocumentAsync(); + if (!templatesDocument.Templates.ContainsKey(name)) + { + return NotFound(); + } - if (!templatesDocument.Templates.ContainsKey(name)) - { - return NotFound(); - } + await (adminTemplates + ? _adminTemplatesManager.RemoveTemplateAsync(name) + : _templatesManager.RemoveTemplateAsync(name)); - await (adminTemplates - ? _adminTemplatesManager.RemoveTemplateAsync(name) - : _templatesManager.RemoveTemplateAsync(name)); + await _notifier.SuccessAsync(H["Template deleted successfully."]); - await _notifier.SuccessAsync(H["Template deleted successfully."]); + return RedirectToReturnUrlOrIndex(returnUrl); + } - return RedirectToReturnUrlOrIndex(returnUrl); + [HttpPost, ActionName("Index")] + [FormValueRequired("submit.BulkAction")] + public async Task ListPost(ContentOptions options, IEnumerable itemIds) + { + if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) + { + return Forbid(); } - [HttpPost, ActionName("Index")] - [FormValueRequired("submit.BulkAction")] - public async Task ListPost(ContentOptions options, IEnumerable itemIds) + if (itemIds?.Count() > 0) { - if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTemplates)) - { - return Forbid(); - } + var templatesDocument = options.AdminTemplates + ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() + : await _templatesManager.LoadTemplatesDocumentAsync(); + var checkedContentItems = templatesDocument.Templates.Where(x => itemIds.Contains(x.Key)); - if (itemIds?.Count() > 0) + switch (options.BulkAction) { - var templatesDocument = options.AdminTemplates - ? await _adminTemplatesManager.LoadTemplatesDocumentAsync() - : await _templatesManager.LoadTemplatesDocumentAsync(); - var checkedContentItems = templatesDocument.Templates.Where(x => itemIds.Contains(x.Key)); - - switch (options.BulkAction) - { - case ContentsBulkAction.None: - break; - case ContentsBulkAction.Remove: - foreach (var item in checkedContentItems) - { - await (options.AdminTemplates - ? _adminTemplatesManager.RemoveTemplateAsync(item.Key) - : _templatesManager.RemoveTemplateAsync(item.Key)); - } - await _notifier.SuccessAsync(H["Templates successfully removed."]); - break; - default: - return BadRequest(); - } + case ContentsBulkAction.None: + break; + case ContentsBulkAction.Remove: + foreach (var item in checkedContentItems) + { + await (options.AdminTemplates + ? _adminTemplatesManager.RemoveTemplateAsync(item.Key) + : _templatesManager.RemoveTemplateAsync(item.Key)); + } + await _notifier.SuccessAsync(H["Templates successfully removed."]); + break; + default: + return BadRequest(); } + } - if (options.AdminTemplates) - { - return RedirectToAction(nameof(Admin)); - } - else - { - return RedirectToAction(nameof(Index)); - } + if (options.AdminTemplates) + { + return RedirectToAction(nameof(Admin)); + } + else + { + return RedirectToAction(nameof(Index)); } + } - private IActionResult RedirectToReturnUrlOrIndex(string returnUrl) + private IActionResult RedirectToReturnUrlOrIndex(string returnUrl) + { + if ((string.IsNullOrEmpty(returnUrl) == false) && (Url.IsLocalUrl(returnUrl))) { - if ((string.IsNullOrEmpty(returnUrl) == false) && (Url.IsLocalUrl(returnUrl))) - { - return this.Redirect(returnUrl, true); - } - else - { - return RedirectToAction(nameof(Index)); - } + return this.Redirect(returnUrl, true); + } + else + { + return RedirectToAction(nameof(Index)); } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentSource.cs index 243f1d83879..80eacda3a78 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentSource.cs @@ -5,50 +5,49 @@ using OrchardCore.Templates.Models; using OrchardCore.Templates.Services; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +public class AllAdminTemplatesDeploymentSource : IDeploymentSource { - public class AllAdminTemplatesDeploymentSource : IDeploymentSource + private readonly AdminTemplatesManager _templatesManager; + + public AllAdminTemplatesDeploymentSource(AdminTemplatesManager templatesManager) { - private readonly AdminTemplatesManager _templatesManager; + _templatesManager = templatesManager; + } - public AllAdminTemplatesDeploymentSource(AdminTemplatesManager templatesManager) + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + if (step is not AllAdminTemplatesDeploymentStep allTemplatesStep) { - _templatesManager = templatesManager; + return; } - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) - { - if (step is not AllAdminTemplatesDeploymentStep allTemplatesStep) - { - return; - } - - var templateObjects = new JsonObject(); - var templates = await _templatesManager.GetTemplatesDocumentAsync(); + var templateObjects = new JsonObject(); + var templates = await _templatesManager.GetTemplatesDocumentAsync(); - if (allTemplatesStep.ExportAsFiles) + if (allTemplatesStep.ExportAsFiles) + { + foreach (var template in templates.Templates) { - foreach (var template in templates.Templates) - { - var fileName = "AdminTemplates/" + template.Key.Replace("__", "-").Replace("_", ".") + ".liquid"; - var templateValue = new Template { Description = template.Value.Description, Content = $"[file:text('{fileName}')]" }; - await result.FileBuilder.SetFileAsync(fileName, Encoding.UTF8.GetBytes(template.Value.Content)); - templateObjects[template.Key] = JObject.FromObject(templateValue); - } + var fileName = "AdminTemplates/" + template.Key.Replace("__", "-").Replace("_", ".") + ".liquid"; + var templateValue = new Template { Description = template.Value.Description, Content = $"[file:text('{fileName}')]" }; + await result.FileBuilder.SetFileAsync(fileName, Encoding.UTF8.GetBytes(template.Value.Content)); + templateObjects[template.Key] = JObject.FromObject(templateValue); } - else + } + else + { + foreach (var template in templates.Templates) { - foreach (var template in templates.Templates) - { - templateObjects[template.Key] = JObject.FromObject(template.Value); - } + templateObjects[template.Key] = JObject.FromObject(template.Value); } - - result.Steps.Add(new JsonObject - { - ["name"] = "AdminTemplates", - ["AdminTemplates"] = templateObjects, - }); } + + result.Steps.Add(new JsonObject + { + ["name"] = "AdminTemplates", + ["AdminTemplates"] = templateObjects, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStep.cs index bf2ce055a54..fcb6d35f057 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStep.cs @@ -1,16 +1,15 @@ using OrchardCore.Deployment; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +/// +/// Adds templates to a . +/// +public class AllAdminTemplatesDeploymentStep : DeploymentStep { - /// - /// Adds templates to a . - /// - public class AllAdminTemplatesDeploymentStep : DeploymentStep + public AllAdminTemplatesDeploymentStep() { - public AllAdminTemplatesDeploymentStep() - { - Name = "AllAdminTemplates"; - } - public bool ExportAsFiles { get; set; } + Name = "AllAdminTemplates"; } + public bool ExportAsFiles { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStepDriver.cs index 14c88380113..c5c35a155cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllAdminTemplatesDeploymentStepDriver.cs @@ -4,31 +4,30 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Templates.ViewModels; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +public sealed class AllAdminTemplatesDeploymentStepDriver : DisplayDriver { - public sealed class AllAdminTemplatesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllAdminTemplatesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllAdminTemplatesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllAdminTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllAdminTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllAdminTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllAdminTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllAdminTemplatesDeploymentStep step, BuildEditorContext context) - { - return Initialize("AllAdminTemplatesDeploymentStep_Fields_Edit", model => - { - model.ExportAsFiles = step.ExportAsFiles; - }).Location("Content"); - } - public override async Task UpdateAsync(AllAdminTemplatesDeploymentStep step, UpdateEditorContext context) + public override IDisplayResult Edit(AllAdminTemplatesDeploymentStep step, BuildEditorContext context) + { + return Initialize("AllAdminTemplatesDeploymentStep_Fields_Edit", model => { - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsFiles); + model.ExportAsFiles = step.ExportAsFiles; + }).Location("Content"); + } + public override async Task UpdateAsync(AllAdminTemplatesDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsFiles); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentSource.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentSource.cs index 83d004cc175..ee26fb837a2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentSource.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentSource.cs @@ -5,52 +5,51 @@ using OrchardCore.Templates.Models; using OrchardCore.Templates.Services; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +public class AllTemplatesDeploymentSource : IDeploymentSource { - public class AllTemplatesDeploymentSource : IDeploymentSource + private readonly TemplatesManager _templatesManager; + + public AllTemplatesDeploymentSource(TemplatesManager templatesManager) { - private readonly TemplatesManager _templatesManager; + _templatesManager = templatesManager; + } - public AllTemplatesDeploymentSource(TemplatesManager templatesManager) - { - _templatesManager = templatesManager; - } + public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + { + var allTemplatesStep = step as AllTemplatesDeploymentStep; - public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlanResult result) + if (allTemplatesStep == null) { - var allTemplatesStep = step as AllTemplatesDeploymentStep; - - if (allTemplatesStep == null) - { - return; - } + return; + } - var templateObjects = new JsonObject(); - var templates = await _templatesManager.GetTemplatesDocumentAsync(); + var templateObjects = new JsonObject(); + var templates = await _templatesManager.GetTemplatesDocumentAsync(); - if (allTemplatesStep.ExportAsFiles) + if (allTemplatesStep.ExportAsFiles) + { + foreach (var template in templates.Templates) { - foreach (var template in templates.Templates) - { - var fileName = "Templates/" + template.Key.Replace("__", "-").Replace("_", ".") + ".liquid"; - var templateValue = new Template { Description = template.Value.Description, Content = $"[file:text('{fileName}')]" }; - await result.FileBuilder.SetFileAsync(fileName, Encoding.UTF8.GetBytes(template.Value.Content)); - templateObjects[template.Key] = JObject.FromObject(templateValue); - } + var fileName = "Templates/" + template.Key.Replace("__", "-").Replace("_", ".") + ".liquid"; + var templateValue = new Template { Description = template.Value.Description, Content = $"[file:text('{fileName}')]" }; + await result.FileBuilder.SetFileAsync(fileName, Encoding.UTF8.GetBytes(template.Value.Content)); + templateObjects[template.Key] = JObject.FromObject(templateValue); } - else + } + else + { + foreach (var template in templates.Templates) { - foreach (var template in templates.Templates) - { - templateObjects[template.Key] = JObject.FromObject(template.Value); - } + templateObjects[template.Key] = JObject.FromObject(template.Value); } - - result.Steps.Add(new JsonObject - { - ["name"] = "Templates", - ["Templates"] = templateObjects, - }); } + + result.Steps.Add(new JsonObject + { + ["name"] = "Templates", + ["Templates"] = templateObjects, + }); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStep.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStep.cs index 089b1a3e210..36ddb8f3ae4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStep.cs @@ -1,16 +1,15 @@ using OrchardCore.Deployment; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +/// +/// Adds templates to a . +/// +public class AllTemplatesDeploymentStep : DeploymentStep { - /// - /// Adds templates to a . - /// - public class AllTemplatesDeploymentStep : DeploymentStep + public AllTemplatesDeploymentStep() { - public AllTemplatesDeploymentStep() - { - Name = "AllTemplates"; - } - public bool ExportAsFiles { get; set; } + Name = "AllTemplates"; } + public bool ExportAsFiles { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStepDriver.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStepDriver.cs index 2f3aeb2424a..5b80adc05cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStepDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Deployment/AllTemplatesDeploymentStepDriver.cs @@ -4,31 +4,30 @@ using OrchardCore.DisplayManagement.Views; using OrchardCore.Templates.ViewModels; -namespace OrchardCore.Templates.Deployment +namespace OrchardCore.Templates.Deployment; + +public sealed class AllTemplatesDeploymentStepDriver : DisplayDriver { - public sealed class AllTemplatesDeploymentStepDriver : DisplayDriver + public override Task DisplayAsync(AllTemplatesDeploymentStep step, BuildDisplayContext context) { - public override Task DisplayAsync(AllTemplatesDeploymentStep step, BuildDisplayContext context) - { - return - CombineAsync( - View("AllTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), - View("AllTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") - ); - } + return + CombineAsync( + View("AllTemplatesDeploymentStep_Summary", step).Location("Summary", "Content"), + View("AllTemplatesDeploymentStep_Thumbnail", step).Location("Thumbnail", "Content") + ); + } - public override IDisplayResult Edit(AllTemplatesDeploymentStep step, BuildEditorContext context) - { - return Initialize("AllTemplatesDeploymentStep_Fields_Edit", model => - { - model.ExportAsFiles = step.ExportAsFiles; - }).Location("Content"); - } - public override async Task UpdateAsync(AllTemplatesDeploymentStep step, UpdateEditorContext context) + public override IDisplayResult Edit(AllTemplatesDeploymentStep step, BuildEditorContext context) + { + return Initialize("AllTemplatesDeploymentStep_Fields_Edit", model => { - await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsFiles); + model.ExportAsFiles = step.ExportAsFiles; + }).Location("Content"); + } + public override async Task UpdateAsync(AllTemplatesDeploymentStep step, UpdateEditorContext context) + { + await context.Updater.TryUpdateModelAsync(step, Prefix, x => x.ExportAsFiles); - return Edit(step, context); - } + return Edit(step, context); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Models/AdminTemplatesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Models/AdminTemplatesDocument.cs index 62265e6fa9d..a93f1d445a6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Models/AdminTemplatesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Models/AdminTemplatesDocument.cs @@ -1,6 +1,5 @@ -namespace OrchardCore.Templates.Models +namespace OrchardCore.Templates.Models; + +public class AdminTemplatesDocument : TemplatesDocument { - public class AdminTemplatesDocument : TemplatesDocument - { - } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Models/TemplatesDocument.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Models/TemplatesDocument.cs index 67d0b857ca2..87774a328d8 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Models/TemplatesDocument.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Models/TemplatesDocument.cs @@ -2,16 +2,15 @@ using System.Collections.Generic; using OrchardCore.Data.Documents; -namespace OrchardCore.Templates.Models +namespace OrchardCore.Templates.Models; + +public class TemplatesDocument : Document { - public class TemplatesDocument : Document - { - public Dictionary Templates { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + public Dictionary Templates { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); +} - public class Template - { - public string Content { get; set; } - public string Description { get; set; } - } +public class Template +{ + public string Content { get; set; } + public string Description { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Templates/Recipes/AdminTemplateStep.cs b/src/OrchardCore.Modules/OrchardCore.Templates/Recipes/AdminTemplateStep.cs index 9c434b8f83a..233dcec2af1 100644 --- a/src/OrchardCore.Modules/OrchardCore.Templates/Recipes/AdminTemplateStep.cs +++ b/src/OrchardCore.Modules/OrchardCore.Templates/Recipes/AdminTemplateStep.cs @@ -6,36 +6,35 @@ using OrchardCore.Templates.Models; using OrchardCore.Templates.Services; -namespace OrchardCore.Templates.Recipes +namespace OrchardCore.Templates.Recipes; + +/// +/// This recipe step creates a set of templates. +/// +public sealed class AdminTemplateStep : IRecipeStepHandler { - /// - /// This recipe step creates a set of templates. - /// - public sealed class AdminTemplateStep : IRecipeStepHandler + private readonly AdminTemplatesManager _adminTemplatesManager; + + public AdminTemplateStep(AdminTemplatesManager templatesManager) { - private readonly AdminTemplatesManager _adminTemplatesManager; + _adminTemplatesManager = templatesManager; + } - public AdminTemplateStep(AdminTemplatesManager templatesManager) + public async Task ExecuteAsync(RecipeExecutionContext context) + { + if (!string.Equals(context.Name, "AdminTemplates", StringComparison.OrdinalIgnoreCase)) { - _adminTemplatesManager = templatesManager; + return; } - public async Task ExecuteAsync(RecipeExecutionContext context) + if (context.Step.TryGetPropertyValue("AdminTemplates", out var jsonNode) && jsonNode is JsonObject templates) { - if (!string.Equals(context.Name, "AdminTemplates", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (context.Step.TryGetPropertyValue("AdminTemplates", out var jsonNode) && jsonNode is JsonObject templates) + foreach (var property in templates) { - foreach (var property in templates) - { - var name = property.Key; - var value = property.Value.ToObject